poise-python 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.kitchen.travis.yml +9 -0
  4. data/.kitchen.yml +8 -0
  5. data/.travis.yml +21 -0
  6. data/.yardopts +7 -0
  7. data/Berksfile +28 -0
  8. data/Gemfile +33 -0
  9. data/LICENSE +201 -0
  10. data/README.md +399 -0
  11. data/Rakefile +17 -0
  12. data/chef/attributes/default.rb +24 -0
  13. data/chef/recipes/default.rb +20 -0
  14. data/lib/poise_python.rb +25 -0
  15. data/lib/poise_python/cheftie.rb +18 -0
  16. data/lib/poise_python/error.rb +23 -0
  17. data/lib/poise_python/python_command_mixin.rb +45 -0
  18. data/lib/poise_python/python_providers.rb +35 -0
  19. data/lib/poise_python/python_providers/base.rb +177 -0
  20. data/lib/poise_python/python_providers/portable_pypy.rb +96 -0
  21. data/lib/poise_python/python_providers/scl.rb +77 -0
  22. data/lib/poise_python/python_providers/system.rb +86 -0
  23. data/lib/poise_python/resources.rb +31 -0
  24. data/lib/poise_python/resources/pip_requirements.rb +102 -0
  25. data/lib/poise_python/resources/python_execute.rb +83 -0
  26. data/lib/poise_python/resources/python_package.rb +322 -0
  27. data/lib/poise_python/resources/python_runtime.rb +114 -0
  28. data/lib/poise_python/resources/python_runtime_pip.rb +167 -0
  29. data/lib/poise_python/resources/python_runtime_test.rb +185 -0
  30. data/lib/poise_python/resources/python_virtualenv.rb +164 -0
  31. data/lib/poise_python/utils.rb +63 -0
  32. data/lib/poise_python/utils/python_encoder.rb +73 -0
  33. data/lib/poise_python/version.rb +20 -0
  34. data/poise-python.gemspec +41 -0
  35. data/test/cookbooks/poise-python_test/metadata.rb +18 -0
  36. data/test/cookbooks/poise-python_test/recipes/default.rb +40 -0
  37. data/test/gemfiles/chef-12.gemfile +19 -0
  38. data/test/gemfiles/master.gemfile +23 -0
  39. data/test/integration/default/serverspec/default_spec.rb +102 -0
  40. data/test/spec/python_command_mixin_spec.rb +115 -0
  41. data/test/spec/python_providers/portable_pypy_spec.rb +68 -0
  42. data/test/spec/python_providers/scl_spec.rb +75 -0
  43. data/test/spec/python_providers/system_spec.rb +81 -0
  44. data/test/spec/resources/pip_requirements_spec.rb +69 -0
  45. data/test/spec/resources/python_package_spec.rb +65 -0
  46. data/test/spec/resources/python_runtime_pip_spec.rb +33 -0
  47. data/test/spec/resources/python_virtualenv_spec.rb +103 -0
  48. data/test/spec/spec_helper.rb +19 -0
  49. data/test/spec/utils/python_encoder_spec.rb +79 -0
  50. data/test/spec/utils_spec.rb +86 -0
  51. 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'