poise-python 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.kitchen.travis.yml +9 -0
- data/.kitchen.yml +8 -0
- data/.travis.yml +21 -0
- data/.yardopts +7 -0
- data/Berksfile +28 -0
- data/Gemfile +33 -0
- data/LICENSE +201 -0
- data/README.md +399 -0
- data/Rakefile +17 -0
- data/chef/attributes/default.rb +24 -0
- data/chef/recipes/default.rb +20 -0
- data/lib/poise_python.rb +25 -0
- data/lib/poise_python/cheftie.rb +18 -0
- data/lib/poise_python/error.rb +23 -0
- data/lib/poise_python/python_command_mixin.rb +45 -0
- data/lib/poise_python/python_providers.rb +35 -0
- data/lib/poise_python/python_providers/base.rb +177 -0
- data/lib/poise_python/python_providers/portable_pypy.rb +96 -0
- data/lib/poise_python/python_providers/scl.rb +77 -0
- data/lib/poise_python/python_providers/system.rb +86 -0
- data/lib/poise_python/resources.rb +31 -0
- data/lib/poise_python/resources/pip_requirements.rb +102 -0
- data/lib/poise_python/resources/python_execute.rb +83 -0
- data/lib/poise_python/resources/python_package.rb +322 -0
- data/lib/poise_python/resources/python_runtime.rb +114 -0
- data/lib/poise_python/resources/python_runtime_pip.rb +167 -0
- data/lib/poise_python/resources/python_runtime_test.rb +185 -0
- data/lib/poise_python/resources/python_virtualenv.rb +164 -0
- data/lib/poise_python/utils.rb +63 -0
- data/lib/poise_python/utils/python_encoder.rb +73 -0
- data/lib/poise_python/version.rb +20 -0
- data/poise-python.gemspec +41 -0
- data/test/cookbooks/poise-python_test/metadata.rb +18 -0
- data/test/cookbooks/poise-python_test/recipes/default.rb +40 -0
- data/test/gemfiles/chef-12.gemfile +19 -0
- data/test/gemfiles/master.gemfile +23 -0
- data/test/integration/default/serverspec/default_spec.rb +102 -0
- data/test/spec/python_command_mixin_spec.rb +115 -0
- data/test/spec/python_providers/portable_pypy_spec.rb +68 -0
- data/test/spec/python_providers/scl_spec.rb +75 -0
- data/test/spec/python_providers/system_spec.rb +81 -0
- data/test/spec/resources/pip_requirements_spec.rb +69 -0
- data/test/spec/resources/python_package_spec.rb +65 -0
- data/test/spec/resources/python_runtime_pip_spec.rb +33 -0
- data/test/spec/resources/python_virtualenv_spec.rb +103 -0
- data/test/spec/spec_helper.rb +19 -0
- data/test/spec/utils/python_encoder_spec.rb +79 -0
- data/test/spec/utils_spec.rb +86 -0
- metadata +170 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'poise'
|
18
|
+
|
19
|
+
# Break a require loop by letting autoload work its magic.
|
20
|
+
require 'poise_python'
|
21
|
+
|
22
|
+
|
23
|
+
module PoisePython
|
24
|
+
module Resources
|
25
|
+
# (see PythonVirtualenv::Resource)
|
26
|
+
# @since 1.0.0
|
27
|
+
module PythonVirtualenv
|
28
|
+
# A `python_virtualenv` resource to manage Python virtual environments.
|
29
|
+
#
|
30
|
+
# @provides python_virtualenv
|
31
|
+
# @action create
|
32
|
+
# @action delete
|
33
|
+
# @example
|
34
|
+
# python_virtualenv '/opt/myapp'
|
35
|
+
class Resource < PoisePython::Resources::PythonRuntime::Resource
|
36
|
+
include PoisePython::PythonCommandMixin
|
37
|
+
provides(:python_virtualenv)
|
38
|
+
# Add create and delete actions as more semantically relevant aliases.
|
39
|
+
default_action(:create)
|
40
|
+
actions(:create, :delete)
|
41
|
+
|
42
|
+
# @!attribute path
|
43
|
+
# Path to create the environment at.
|
44
|
+
# @return [String]
|
45
|
+
attribute(:path, kind_of: String, name_attribute: true)
|
46
|
+
# @!attribute group
|
47
|
+
# System group to create the virtualenv.
|
48
|
+
# @return [String, Integer, nil]
|
49
|
+
attribute(:group, kind_of: [String, Integer, NilClass])
|
50
|
+
# @!attribute system_site_packages
|
51
|
+
# Enable or disable visibilty of system packages in the environment.
|
52
|
+
# @return [Boolean]
|
53
|
+
attribute(:system_site_packages, equal_to: [true, false], default: false)
|
54
|
+
# @!attribute user
|
55
|
+
# System user to create the virtualenv.
|
56
|
+
# @return [String, Integer, nil]
|
57
|
+
attribute(:user, kind_of: [String, Integer, NilClass])
|
58
|
+
|
59
|
+
# Lock the default provider.
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def initialize(*args)
|
63
|
+
super
|
64
|
+
# Sidestep all the normal provider lookup stuffs. This is kind of
|
65
|
+
# gross but it will do for now. The hard part is that the base classes
|
66
|
+
# for the resource and provider are using Poise::Inversion, which we
|
67
|
+
# don't want to use for python_virtualenv.
|
68
|
+
@provider = Provider
|
69
|
+
end
|
70
|
+
|
71
|
+
# Upstream attribute we don't support. Sets are an error and gets always
|
72
|
+
# return nil.
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
# @param arg [Object] Ignored
|
76
|
+
# @return [nil]
|
77
|
+
def version(arg=nil)
|
78
|
+
raise NoMethodError if arg
|
79
|
+
end
|
80
|
+
|
81
|
+
# (see #version)
|
82
|
+
def virtualenv_version(arg=nil)
|
83
|
+
raise NoMethodError if arg
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# The default provider for `python_virtualenv`.
|
88
|
+
#
|
89
|
+
# @see Resource
|
90
|
+
# @provides python_virtualenv
|
91
|
+
class Provider < PoisePython::PythonProviders::Base
|
92
|
+
include PoisePython::PythonCommandMixin
|
93
|
+
provides(:python_virtualenv)
|
94
|
+
|
95
|
+
# Alias our actions. Slightly annoying that they will show in
|
96
|
+
# tracebacks with the original names, but oh well.
|
97
|
+
alias_method :action_create, :action_install
|
98
|
+
alias_method :action_delete, :action_uninstall
|
99
|
+
|
100
|
+
def python_binary
|
101
|
+
::File.join(new_resource.path, 'bin', 'python')
|
102
|
+
end
|
103
|
+
|
104
|
+
def python_environment
|
105
|
+
if new_resource.parent_python
|
106
|
+
new_resource.parent_python.python_environment
|
107
|
+
else
|
108
|
+
{}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def install_python
|
115
|
+
return if ::File.exist?(new_resource.path)
|
116
|
+
|
117
|
+
cmd = python_shell_out(%w{-m venv -h})
|
118
|
+
if cmd.error?
|
119
|
+
converge_by("Creating virtualenv at #{new_resource.path}") do
|
120
|
+
create_virtualenv(%w{virtualenv})
|
121
|
+
end
|
122
|
+
else
|
123
|
+
converge_by("Creating venv at #{new_resource.path}") do
|
124
|
+
create_virtualenv(%w{venv --without-pip})
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def uninstall_python
|
130
|
+
directory new_resource.path do
|
131
|
+
action :delete
|
132
|
+
recursive true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Don't install virtualenv inside virtualenv.
|
137
|
+
#
|
138
|
+
# @api private
|
139
|
+
# @return [void]
|
140
|
+
def install_virtualenv
|
141
|
+
# This space left intentionally blank.
|
142
|
+
end
|
143
|
+
|
144
|
+
# Create a virtualenv using virtualenv or venv.
|
145
|
+
#
|
146
|
+
# @param driver [Array<String>] Command snippet to actually make it.
|
147
|
+
# @return [void]
|
148
|
+
def create_virtualenv(driver)
|
149
|
+
cmd = %w{-m} + driver
|
150
|
+
cmd << '--system-site-packages' if new_resource.system_site_packages
|
151
|
+
cmd << new_resource.path
|
152
|
+
python_shell_out!(cmd, environment: {
|
153
|
+
# Use the environment variables to cope with older virtualenv not
|
154
|
+
# supporting --no-wheel. The env var will be ignored if unsupported.
|
155
|
+
'VIRTUALENV_NO_PIP' => '1',
|
156
|
+
'VIRTUALENV_NO_SETUPTOOLS' => '1',
|
157
|
+
'VIRTUALENV_NO_WHEEL' => '1',
|
158
|
+
}, group: new_resource.group, user: new_resource.user)
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'poise_python/error'
|
18
|
+
|
19
|
+
|
20
|
+
module PoisePython
|
21
|
+
# Helper methods for Python-related things.
|
22
|
+
#
|
23
|
+
# @since 1.0.0
|
24
|
+
module Utils
|
25
|
+
autoload :PythonEncoder, 'poise_python/utils/python_encoder'
|
26
|
+
extend self
|
27
|
+
|
28
|
+
# Convert an object to a Python literal.
|
29
|
+
#
|
30
|
+
# @param obj [Object] Ovject to convert.
|
31
|
+
# @return [String]
|
32
|
+
def to_python(obj)
|
33
|
+
PythonEncoder.new(obj).encode
|
34
|
+
end
|
35
|
+
|
36
|
+
# Convert path to a Python dotted module name.
|
37
|
+
#
|
38
|
+
# @param path [String] Path to the file. If base is not given, this must be
|
39
|
+
# a relative path.
|
40
|
+
# @param base [String] Optional base path to treat the file as relative to.
|
41
|
+
# @return [String]
|
42
|
+
def path_to_module(path, base=nil)
|
43
|
+
if base
|
44
|
+
path = ::File.expand_path(path, base)
|
45
|
+
raise PoisePython::Error.new("Path #{path} is not inside base path #{base}") unless path.start_with?(base)
|
46
|
+
path = path[base.length+1..-1]
|
47
|
+
end
|
48
|
+
path = path[0..-4] if path.end_with?('.py')
|
49
|
+
path.gsub(/#{::File::SEPARATOR}/, '.')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Convert a Python dotted module name to a path.
|
53
|
+
#
|
54
|
+
# @param mod [String] Dotted module name.
|
55
|
+
# @param base [String] Optional base path to treat the file as relative to.
|
56
|
+
# @return [String]
|
57
|
+
def module_to_path(mod, base=nil)
|
58
|
+
path = mod.gsub(/\./, ::File::SEPARATOR) + '.py'
|
59
|
+
path = ::File.join(base, path) if base
|
60
|
+
path
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
|
19
|
+
|
20
|
+
module PoisePython
|
21
|
+
module Utils
|
22
|
+
# Convert Ruby data structures to a Python literal. Overall similar to JSON
|
23
|
+
# but just different enough that I need to write this. Thanks Obama.
|
24
|
+
#
|
25
|
+
# @since 1.0.0
|
26
|
+
# @api private
|
27
|
+
class PythonEncoder
|
28
|
+
def initialize(root, depth_limit: 100)
|
29
|
+
@root = root
|
30
|
+
@depth_limit = depth_limit
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode
|
34
|
+
encode_obj(@root, 0)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def encode_obj(obj, depth)
|
40
|
+
raise ArgumentError.new("Depth limit exceeded") if depth > @depth_limit
|
41
|
+
case obj
|
42
|
+
when Hash
|
43
|
+
encode_hash(obj, depth)
|
44
|
+
when Array
|
45
|
+
encode_array(obj, depth)
|
46
|
+
when true
|
47
|
+
'True'
|
48
|
+
when false
|
49
|
+
'False'
|
50
|
+
when nil
|
51
|
+
'None'
|
52
|
+
else
|
53
|
+
obj.to_json
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def encode_hash(obj, depth)
|
58
|
+
middle = obj.map do |key, value|
|
59
|
+
"#{encode_obj(key, depth+1)}:#{encode_obj(value, depth+1)}"
|
60
|
+
end
|
61
|
+
"{#{middle.join(',')}}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def encode_array(obj, depth)
|
65
|
+
middle = obj.map do |value|
|
66
|
+
encode_obj(value, depth+1)
|
67
|
+
end
|
68
|
+
"[#{middle.join(',')}]"
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
|
18
|
+
module PoisePython
|
19
|
+
VERSION = '1.0.0'
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
lib = File.expand_path('../lib', __FILE__)
|
18
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
19
|
+
require 'poise_python/version'
|
20
|
+
|
21
|
+
Gem::Specification.new do |spec|
|
22
|
+
spec.name = 'poise-python'
|
23
|
+
spec.version = PoisePython::VERSION
|
24
|
+
spec.authors = ['Noah Kantrowitz']
|
25
|
+
spec.email = %w{noah@coderanger.net}
|
26
|
+
spec.description = "A Chef cookbook for managing Python installations."
|
27
|
+
spec.summary = spec.description
|
28
|
+
spec.homepage = 'https://github.com/poise/python'
|
29
|
+
spec.license = 'Apache 2.0'
|
30
|
+
|
31
|
+
spec.files = `git ls-files`.split($/)
|
32
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
33
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
34
|
+
spec.require_paths = %w{lib}
|
35
|
+
|
36
|
+
spec.add_dependency 'halite', '~> 1.0'
|
37
|
+
spec.add_dependency 'poise', '~> 2.0'
|
38
|
+
spec.add_dependency 'poise-languages', '~> 1.0.dev'
|
39
|
+
|
40
|
+
spec.add_development_dependency 'poise-boiler', '~> 1.0'
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
name 'poise-python_test'
|
18
|
+
depends 'poise-python'
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require 'poise_python/resources/python_runtime_test'
|
18
|
+
|
19
|
+
# Install lsb-release because Debian 6 doesn't by default and serverspec requires it
|
20
|
+
package 'lsb-release' if platform?('debian') && node['platform_version'].start_with?('6')
|
21
|
+
|
22
|
+
python_runtime_test '2'
|
23
|
+
|
24
|
+
python_runtime_test '3'
|
25
|
+
|
26
|
+
python_runtime_test 'pypy'
|
27
|
+
|
28
|
+
python_runtime_test 'system' do
|
29
|
+
version ''
|
30
|
+
runtime_provider :system
|
31
|
+
end
|
32
|
+
|
33
|
+
if platform_family?('rhel')
|
34
|
+
python_runtime_test 'scl' do
|
35
|
+
version ''
|
36
|
+
runtime_provider :scl
|
37
|
+
end
|
38
|
+
else
|
39
|
+
file '/no_scl'
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2015, Noah Kantrowitz
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
eval_gemfile File.expand_path('../../../Gemfile', __FILE__)
|
18
|
+
|
19
|
+
gem 'chef', '~> 12.0'
|