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,69 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Resources::PipRequirements do
20
+ let(:pip_cmd) { }
21
+ let(:pip_output) { '' }
22
+ step_into(:pip_requirements)
23
+ before do
24
+ allow(File).to receive(:directory?).and_return(false)
25
+ allow(File).to receive(:directory?).with('/test').and_return(true)
26
+ end
27
+ before do
28
+ expect_any_instance_of(PoisePython::Resources::PipRequirements::Provider).to receive(:python_shell_out!).with(pip_cmd).and_return(double(stdout: pip_output))
29
+ end
30
+
31
+ context 'with a directory' do
32
+ let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/requirements.txt} }
33
+ recipe do
34
+ pip_requirements '/test'
35
+ end
36
+
37
+ it { is_expected.to install_pip_requirements('/test') }
38
+ end # /context with a directory
39
+
40
+ context 'with a file' do
41
+ let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/reqs.txt} }
42
+ recipe do
43
+ pip_requirements '/test/reqs.txt'
44
+ end
45
+
46
+ it { is_expected.to install_pip_requirements('/test/reqs.txt') }
47
+ end # /context with a file
48
+
49
+ context 'action :upgrade' do
50
+ let(:pip_cmd) { %w{-m pip.__main__ install --upgrade --requirement /test/requirements.txt} }
51
+ recipe do
52
+ pip_requirements '/test' do
53
+ action :upgrade
54
+ end
55
+ end
56
+
57
+ it { is_expected.to upgrade_pip_requirements('/test') }
58
+ end # /context action :upgrade
59
+
60
+ context 'with output' do
61
+ let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/requirements.txt} }
62
+ let(:pip_output) { 'Successfully installed' }
63
+ recipe do
64
+ pip_requirements '/test'
65
+ end
66
+
67
+ it { is_expected.to install_pip_requirements('/test').with(updated?: true) }
68
+ end # /context with output
69
+ end
@@ -0,0 +1,65 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Resources::PythonPackage do
20
+ describe PoisePython::Resources::PythonPackage::Resource do
21
+ end # /describe PoisePython::Resources::PythonPackage::Resource
22
+
23
+ describe PoisePython::Resources::PythonPackage::Provider do
24
+ let(:test_provider) { described_class.new(nil, nil) }
25
+
26
+ describe '#parse_pip_outdated' do
27
+ let(:text) { '' }
28
+ subject { test_provider.send(:parse_pip_outdated, text) }
29
+
30
+ context 'with no content' do
31
+ it { is_expected.to eq({}) }
32
+ end # /context with no content
33
+
34
+ context 'with standard content' do
35
+ let(:text) { <<-EOH }
36
+ boto (Current: 2.25.0 Latest: 2.38.0 [wheel])
37
+ botocore (Current: 0.56.0 Latest: 1.1.1 [wheel])
38
+ certifi (Current: 14.5.14 Latest: 2015.4.28 [wheel])
39
+ cffi (Current: 0.8.1 Latest: 1.1.2 [sdist])
40
+ Fabric (Current: 1.9.1 Latest: 1.10.2 [wheel])
41
+ EOH
42
+ it { is_expected.to eq({'boto' => '2.38.0', 'botocore' => '1.1.1', 'certifi' => '2015.4.28', 'cffi' => '1.1.2', 'fabric' => '1.10.2'}) }
43
+ end # /context with standard content
44
+ end # /describe #parse_pip_outdated
45
+
46
+ describe '#parse_pip_list' do
47
+ let(:text) { '' }
48
+ subject { test_provider.send(:parse_pip_list, text) }
49
+
50
+ context 'with no content' do
51
+ it { is_expected.to eq({}) }
52
+ end # /context with no content
53
+
54
+ context 'with standard content' do
55
+ let(:text) { <<-EOH }
56
+ eventlet (0.12.1)
57
+ Fabric (1.9.1)
58
+ fabric-rundeck (1.2, /Users/coderanger/src/bal/fabric-rundeck)
59
+ flake8 (2.1.0.dev0)
60
+ EOH
61
+ it { is_expected.to eq({'eventlet' => '0.12.1', 'fabric' => '1.9.1', 'fabric-rundeck' => '1.2', 'flake8' => '2.1.0.dev0'}) }
62
+ end # /context with standard content
63
+ end # /describe #parse_pip_list
64
+ end # /describe PoisePython::Resources::PythonPackage::Provider
65
+ end
@@ -0,0 +1,33 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Resources::PythonRuntimePip do
20
+ step_into(:python_runtime_pip)
21
+ recipe do
22
+ python_runtime 'test'
23
+ python_runtime_pip 'test'
24
+ end
25
+ before do
26
+ provider = PoisePython::Resources::PythonRuntimePip::Provider
27
+ allow_any_instance_of(provider).to receive(:pip_version).and_return(nil)
28
+ allow_any_instance_of(provider).to receive(:bootstrap_pip)
29
+ end
30
+
31
+ # Make sure it can at least vaguely run.
32
+ it { chef_run }
33
+ end
@@ -0,0 +1,103 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Resources::PythonVirtualenv do
20
+ step_into(:python_virtualenv)
21
+ let(:expect_cmd) { nil }
22
+ let(:has_venv) { false }
23
+ let(:expect_user) { nil }
24
+ before do
25
+ if expect_cmd
26
+ expect_any_instance_of(PoisePython::Resources::PythonVirtualenv::Provider).to receive(:python_shell_out).with(%w{-m venv -h}).and_return(double(error?: !has_venv))
27
+ expect_any_instance_of(PoisePython::Resources::PythonVirtualenv::Provider).to receive(:python_shell_out!).with(expect_cmd, environment: be_a(Hash), user: expect_user, group: expect_user)
28
+ end
29
+ end
30
+
31
+ context 'without venv' do
32
+ let(:expect_cmd) { %w{-m virtualenv /test} }
33
+ recipe do
34
+ python_virtualenv '/test'
35
+ end
36
+
37
+ it { is_expected.to create_python_virtualenv('/test') }
38
+ it { expect(chef_run.python_virtualenv('/test').python_binary).to eq '/test/bin/python' }
39
+ it { expect(chef_run.python_virtualenv('/test').python_environment).to eq({}) }
40
+ end # /context without venv
41
+
42
+ context 'with venv' do
43
+ let(:has_venv) { true }
44
+ let(:expect_cmd) { %w{-m venv --without-pip /test} }
45
+ recipe do
46
+ python_virtualenv '/test'
47
+ end
48
+
49
+ it { is_expected.to create_python_virtualenv('/test') }
50
+ end # /context with venv
51
+
52
+ context 'with system_site_packages' do
53
+ let(:expect_cmd) { %w{-m virtualenv --system-site-packages /test} }
54
+ recipe do
55
+ python_virtualenv '/test' do
56
+ system_site_packages true
57
+ end
58
+ end
59
+
60
+ it { is_expected.to create_python_virtualenv('/test') }
61
+ end # /context with system_site_packages
62
+
63
+ context 'with a user and group' do
64
+ let(:expect_cmd) { %w{-m virtualenv /test} }
65
+ let(:expect_user) { 'me' }
66
+ recipe do
67
+ python_virtualenv '/test' do
68
+ user 'me'
69
+ group 'me'
70
+ end
71
+ end
72
+
73
+ it { is_expected.to create_python_virtualenv('/test') }
74
+ end # /context with a user and group
75
+
76
+
77
+ context 'with action :delete' do
78
+ recipe do
79
+ python_virtualenv '/test' do
80
+ action :delete
81
+ end
82
+ end
83
+
84
+ it { is_expected.to delete_python_virtualenv('/test') }
85
+ it { is_expected.to delete_directory('/test') }
86
+ end # /context with action :delete
87
+
88
+ context 'with a parent Python' do
89
+ let(:expect_cmd) { %w{-m virtualenv /test} }
90
+ recipe do
91
+ python_runtime '2' do
92
+ def self.python_environment
93
+ {'KEY' => 'VALUE'}
94
+ end
95
+ end
96
+ python_virtualenv '/test'
97
+ end
98
+
99
+ it { is_expected.to create_python_virtualenv('/test') }
100
+ it { expect(chef_run.python_virtualenv('/test').python_binary).to eq '/test/bin/python' }
101
+ it { expect(chef_run.python_virtualenv('/test').python_environment).to eq({'KEY' => 'VALUE'}) }
102
+ end # /context with a parent Python
103
+ 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
+ require 'poise_boiler/spec_helper'
18
+ require 'poise_python'
19
+ require 'poise_python/cheftie'
@@ -0,0 +1,79 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Utils::PythonEncoder do
20
+ let(:obj) { nil }
21
+ subject { described_class.new(obj).encode }
22
+
23
+ context 'with a string' do
24
+ let(:obj) { 'foobar' }
25
+ it { is_expected.to eq '"foobar"' }
26
+ end # /context with a string
27
+
28
+ context 'with a complicated string' do
29
+ let(:obj) { "im\nalittle\"teapot'" }
30
+ it { is_expected.to eq '"im\\nalittle\\"teapot\'"' }
31
+ end # /context with a complicated string
32
+
33
+ context 'with an integer' do
34
+ let(:obj) { 123 }
35
+ it { is_expected.to eq '123' }
36
+ end # /context with an integer
37
+
38
+ context 'with a float' do
39
+ let(:obj) { 1.3 }
40
+ it { is_expected.to eq '1.3' }
41
+ end # /context with a float
42
+
43
+ context 'with a hash' do
44
+ let(:obj) { {foo: 'bar'} }
45
+ it { is_expected.to eq '{"foo":"bar"}' }
46
+ end # /context with a hash
47
+
48
+ context 'with an array' do
49
+ let(:obj) { ['foo', 1, 'bar'] }
50
+ it { is_expected.to eq '["foo",1,"bar"]' }
51
+ end # /context with an array
52
+
53
+ context 'with true' do
54
+ let(:obj) { true }
55
+ it { is_expected.to eq 'True' }
56
+ end # /context with true
57
+
58
+ context 'with false' do
59
+ let(:obj) { false }
60
+ it { is_expected.to eq 'False' }
61
+ end # /context with false
62
+
63
+ context 'with nil' do
64
+ let(:obj) { nil }
65
+ it { is_expected.to eq 'None' }
66
+ end # /context with nil
67
+
68
+ context 'with a broken object' do
69
+ let(:obj) do
70
+ {}.tap {|obj| obj[:x] = obj }
71
+ end
72
+ it { expect { subject }.to raise_error ArgumentError }
73
+ end # /context with a broken object
74
+
75
+ context 'with a complex object' do
76
+ let(:obj) { {a: [1, "2", true], b: false, c: {d: nil}} }
77
+ it { is_expected.to eq '{"a":[1,"2",True],"b":False,"c":{"d":None}}' }
78
+ end # /context with a complex object
79
+ end
@@ -0,0 +1,86 @@
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 'spec_helper'
18
+
19
+ describe PoisePython::Utils do
20
+ describe '.to_python' do
21
+ subject { described_class.to_python(1) }
22
+ it { is_expected.to eq '1' }
23
+ # More detailed encoder specs in python_encoder_spec.rb
24
+ end # /describe .to_python
25
+
26
+ describe '.path_to_module' do
27
+ let(:path) { '' }
28
+ let(:base) { nil }
29
+ subject { described_class.path_to_module(path, base) }
30
+
31
+ context 'with a relative path' do
32
+ let(:path) { 'foo.py' }
33
+ it { is_expected.to eq 'foo' }
34
+ end # /context with a relative path
35
+
36
+ context 'with a nested relative path' do
37
+ let(:path) { File.join('foo', 'bar', 'baz.py') }
38
+ it { is_expected.to eq 'foo.bar.baz' }
39
+ end # /context with a nested relative path
40
+
41
+ context 'with a non-.py file' do
42
+ let(:path) { File.join('foo', 'bar', 'baz') }
43
+ it { is_expected.to eq 'foo.bar.baz' }
44
+ end # /context with a non-.py file
45
+
46
+ context 'with a base path' do
47
+ let(:path) { File.join('', 'foo', 'bar', 'baz.py') }
48
+ let(:base) { File.join('', 'foo') }
49
+ it { is_expected.to eq 'bar.baz' }
50
+ end # /context with a base path
51
+
52
+ context 'with a base path that does not match the path' do
53
+ let(:path) { File.join('', 'foo', 'bar', 'baz.py') }
54
+ let(:base) { File.join('', 'bar') }
55
+ it { expect { subject }.to raise_error PoisePython::Error }
56
+ end # /context with a base path that does not match the path
57
+
58
+ context 'with a base and relative path' do
59
+ let(:path) { File.join('bar', 'baz.py') }
60
+ let(:base) { File.join('', 'foo') }
61
+ it { is_expected.to eq 'bar.baz' }
62
+ end # /context with a base and relative path
63
+ end # /describe .path_to_module
64
+
65
+ describe '.module_to_path' do
66
+ let(:mod) { '' }
67
+ let(:base) { nil }
68
+ subject { described_class.module_to_path(mod, base) }
69
+
70
+ context 'with a module' do
71
+ let(:mod) { 'foo' }
72
+ it { is_expected.to eq 'foo.py' }
73
+ end # /context with a module
74
+
75
+ context 'with a nested module' do
76
+ let(:mod) { 'foo.bar.baz' }
77
+ it { is_expected.to eq File.join('foo', 'bar', 'baz.py') }
78
+ end # /context with a nested module
79
+
80
+ context 'with a base path' do
81
+ let(:mod) { 'bar.baz' }
82
+ let(:base) { File.join('', 'foo') }
83
+ it { is_expected.to eq File.join('', 'foo', 'bar', 'baz.py') }
84
+ end # /context with a base path
85
+ end # /describe .module_to_path
86
+ end