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,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