poise-python 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'