poise-python 1.1.2 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 776c3bc6bc978dd6a8452e62839af86ea53c0b27
4
- data.tar.gz: e1c625316e1f87a11e25dd96887285c9fd9f7728
3
+ metadata.gz: 6f954d22962ad5f19fbefef8ce992f18bc0f8b73
4
+ data.tar.gz: 91a35461eaecb05a6e3e5dee93e9b4d551d95403
5
5
  SHA512:
6
- metadata.gz: 5bc9ed10105ec2fbad61011761331ec83932efdcd13e38e308749e467de44a72a86a3f487d55a1ced0f55bb61737b2a2c385c0ba9e203aecba78c776b5497b18
7
- data.tar.gz: a8dd9182bfd9d610511831bb6a1c54ac90a30d79778d5a807a750dded125438d2bcadba5e5849dde8081345450eb751a2fefab8caecaa56ad8dc934338da200d
6
+ metadata.gz: b108843aca7b5c9f6c4db0f991c88c6179c8b91f406bcb44ae4df0b90ff02106080aa4e53610454f2ce4108c00238b973f119ce4f46cbea88a9eaef47be17c2a
7
+ data.tar.gz: 60273ff1bc570326fee7b2875210ff4f748dd0620332aeb3bbaa211a83d64ec514c63453f1c216f5444ef5f8f81817c1d286b8837c2f49e9151d3024facb6952
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Poise-Python Changelog
2
2
 
3
+ ## v1.2.0
4
+
5
+ * Add support for passing `user` and `group` to `pip_requirements`.
6
+ * Allow passing a virtualenv resource object to the `virtualenv` property.
7
+ * Update PyPy release versions.
8
+ * Make the `python_virtualenv` resource check for `./bin/python` for idempotence
9
+ instead of the base path.
10
+ * Support for packages with extras in `python_package`.
11
+ * Support for point releases (7.1, 8.1, etc) of Debian in the `system` provider.
12
+
3
13
  ## v1.1.2
4
14
 
5
15
  * Fix `PythonPackage#response_file_variables` for the Chef 12.6 initializer.
data/README.md CHANGED
@@ -277,9 +277,11 @@ notifications will only be triggered if a package is actually installed.
277
277
 
278
278
  * `path` – Path to the requirements file, or a folder containing the
279
279
  requirements file. *(name property)*
280
+ * `group` – System group to install the packages.
280
281
  * `python` – Name of the `python_runtime` resource to use. If not specified, the
281
282
  most recently declared `python_runtime` will be used. Can also be set to the
282
283
  full path to a `python` binary.
284
+ * `user` – System user to install the packages.
283
285
  * `virtualenv` – Name of the `python_virtualenv` resource to use. This is
284
286
  mutually exclusive with the `python` property.
285
287
 
@@ -34,7 +34,11 @@ module PoisePython
34
34
  # @param name [String] Name of the virtualenv resource.
35
35
  # @return [void]
36
36
  def virtualenv(name)
37
- parent_python("python_virtualenv[#{name}]")
37
+ if name.is_a?(PoisePython::Resources::PythonVirtualenv::Resource)
38
+ parent_python(name)
39
+ else
40
+ parent_python("python_virtualenv[#{name}]")
41
+ end
38
42
  end
39
43
  end
40
44
 
@@ -31,6 +31,7 @@ module PoisePython
31
31
  autoload :Base, 'poise_python/python_providers/base'
32
32
 
33
33
  Chef::Platform::ProviderPriorityMap.instance.priority(:python_runtime, [
34
+ PoisePython::PythonProviders::Dummy,
34
35
  PoisePython::PythonProviders::PortablePyPy3,
35
36
  PoisePython::PythonProviders::PortablePyPy,
36
37
  PoisePython::PythonProviders::Scl,
@@ -27,9 +27,18 @@ module PoisePython
27
27
  class Dummy < Base
28
28
  provides(:dummy)
29
29
 
30
+ # Enable by default on ChefSpec.
31
+ #
32
+ # @api private
33
+ def self.provides_auto?(node, _resource)
34
+ node.platform?('chefspec')
35
+ end
36
+
37
+ # Manual overrides for dummy data.
38
+ #
39
+ # @api private
30
40
  def self.default_inversion_options(node, resource)
31
41
  super.merge({
32
- # Manual overrides for dummy data.
33
42
  python_binary: ::File.join('', 'python'),
34
43
  python_environment: nil,
35
44
  })
@@ -26,7 +26,7 @@ module PoisePython
26
26
  provides(:portable_pypy)
27
27
  include PoiseLanguages::Static(
28
28
  name: 'pypy',
29
- versions: %w{2.6 2.5.1 2.5 2.4 2.3.1 2.3 2.2.1 2.2 2.1 2.0.2},
29
+ versions: %w{4.0.1 2.6.1 2.5.1 2.5 2.4 2.3.1 2.3 2.2.1 2.2 2.1 2.0.2},
30
30
  machines: %w{linux-i686 linux-x86_64},
31
31
  url: 'https://bitbucket.org/squeaky/portable-pypy/downloads/pypy-%{version}-%{kernel}_%{machine}-portable.tar.bz2'
32
32
  )
@@ -28,9 +28,9 @@ module PoisePython
28
28
  provides(:system)
29
29
  packages('python', {
30
30
  debian: {
31
- '8' => %w{python3.4 python2.7},
32
- '7' => %w{python3.2 python2.7 python2.6},
33
- '6' => %w{python3.1 python2.6 python2.5},
31
+ '~> 8.0' => %w{python3.4 python2.7},
32
+ '~> 7.0' => %w{python3.2 python2.7 python2.6},
33
+ '~> 6.0' => %w{python3.1 python2.6 python2.5},
34
34
  },
35
35
  ubuntu: {
36
36
  '14.04' => %w{python3.4 python2.7},
@@ -44,6 +44,14 @@ module PoisePython
44
44
  # requirements file.
45
45
  # @return [String]
46
46
  attribute(:path, kind_of: String, name_attribute: true)
47
+ # @!attribute group
48
+ # System group to install the package.
49
+ # @return [String, Integer, nil]
50
+ attribute(:group, kind_of: [String, Integer, NilClass])
51
+ # @!attribute user
52
+ # System user to install the package.
53
+ # @return [String, Integer, nil]
54
+ attribute(:user, kind_of: [String, Integer, NilClass])
47
55
  end
48
56
 
49
57
  # The default provider for `pip_requirements`.
@@ -80,7 +88,7 @@ module PoisePython
80
88
  cmd << '--upgrade' if upgrade
81
89
  cmd << '--requirement'
82
90
  cmd << requirements_path
83
- output = python_shell_out!(cmd).stdout
91
+ output = python_shell_out!(cmd, user: new_resource.user, group: new_resource.group).stdout
84
92
  if output.include?('Successfully installed')
85
93
  new_resource.updated_by_last_action(true)
86
94
  end
@@ -91,7 +91,6 @@ EOH
91
91
  Poise::Helpers::ChefspecMatchers.create_matcher(:python_package, action)
92
92
  end
93
93
 
94
-
95
94
  # @!attribute group
96
95
  # System group to install the package.
97
96
  # @return [String, Integer, nil]
@@ -165,7 +164,7 @@ EOH
165
164
  version_data[name][:current] = current
166
165
  end
167
166
  # Check for newer candidates.
168
- outdated = pip_outdated(pip_requirements(resource.package_name, version)).stdout
167
+ outdated = pip_outdated(pip_requirements(resource.package_name, version, parse: true)).stdout
169
168
  parse_pip_outdated(outdated).each do |name, candidate|
170
169
  # Merge candidates in to the existing versions.
171
170
  version_data[name][:candidate] = candidate
@@ -176,13 +175,13 @@ EOH
176
175
  @candidate_version = []
177
176
  versions = []
178
177
  [resource.package_name].flatten.each do |name|
179
- ver = version_data[name.downcase]
178
+ ver = version_data[parse_package_name(name).downcase]
180
179
  versions << ver[:current]
181
180
  @candidate_version << ver[:candidate]
182
181
  end
183
182
  resource.version(versions)
184
183
  else
185
- ver = version_data[resource.package_name.downcase]
184
+ ver = version_data[parse_package_name(resource.package_name).downcase]
186
185
  resource.version(ver[:current])
187
186
  @candidate_version = ver[:candidate]
188
187
  end
@@ -223,11 +222,16 @@ EOH
223
222
  # @param name [String, Array<String>] Name or names for the packages.
224
223
  # @param version [String, Array<String>] Version or versions for the
225
224
  # packages.
225
+ # @param parse [Boolean] Use parsed package names.
226
226
  # @return [Array<String>]
227
- def pip_requirements(name, version)
227
+ def pip_requirements(name, version, parse: false)
228
228
  [name].flatten.zip([version].flatten).map do |n, v|
229
+ n = parse_package_name(n) if parse
229
230
  v = v.to_s.strip
230
- if v.empty?
231
+ if n =~ /:\/\//
232
+ # Probably a URI.
233
+ n
234
+ elsif v.empty?
231
235
  # No version requirement, send through unmodified.
232
236
  n
233
237
  elsif v =~ /^\d/
@@ -322,6 +326,25 @@ EOH
322
326
  end
323
327
  end
324
328
 
329
+ # Regexp for package URLs.
330
+ PACKAGE_NAME_URL = /:\/\/.*?#egg=(.*)$/
331
+
332
+ # Regexp for extras.
333
+ PACKAGE_NAME_EXTRA = /^(.*?)\[.*?\]$/
334
+
335
+ # Find the underlying name from a pip input sequence.
336
+ #
337
+ # @param raw_name [String] Raw package name.
338
+ # @return [String]
339
+ def parse_package_name(raw_name)
340
+ case raw_name
341
+ when PACKAGE_NAME_URL, PACKAGE_NAME_EXTRA
342
+ $1
343
+ else
344
+ raw_name
345
+ end
346
+ end
347
+
325
348
  end
326
349
  end
327
350
  end
@@ -40,7 +40,7 @@ module PoisePython
40
40
  attribute(:path, kind_of: String, default: lazy { default_path })
41
41
 
42
42
  def default_path
43
- ::File.join('', 'root', "python_test_#{name}")
43
+ ::File.join('', 'opt', "python_test_#{name}")
44
44
  end
45
45
  end
46
46
 
@@ -58,7 +58,9 @@ module PoisePython
58
58
  def action_run
59
59
  notifying_block do
60
60
  # Top level directory for this test.
61
- directory new_resource.path
61
+ directory new_resource.path do
62
+ mode '777'
63
+ end
62
64
 
63
65
  # Install and log the version.
64
66
  python_runtime new_resource.name do
@@ -128,6 +130,34 @@ EOH
128
130
  end
129
131
  test_import('requests')
130
132
  test_import('six')
133
+
134
+ # Create a non-root user and test installing with it.
135
+ test_user = "py#{new_resource.name}"
136
+ test_home = ::File.join('', 'home', test_user)
137
+ group test_user do
138
+ system true
139
+ end
140
+ user test_user do
141
+ comment "Test user for python_runtime_test #{new_resource.name}"
142
+ gid test_user
143
+ home test_home
144
+ shell '/bin/false'
145
+ system true
146
+ end
147
+ directory test_home do
148
+ mode '700'
149
+ group test_user
150
+ user test_user
151
+ end
152
+ test_venv = python_virtualenv ::File.join(test_home, 'env') do
153
+ python new_resource.name
154
+ user test_user
155
+ end
156
+ python_package 'attrs' do
157
+ user test_user
158
+ virtualenv test_venv
159
+ end
160
+ test_import('attrs', 'attr', python: nil, virtualenv: test_venv, user: test_user)
131
161
  end
132
162
  end
133
163
 
@@ -157,7 +187,7 @@ EOH
157
187
  end
158
188
  end
159
189
 
160
- def test_import(name, path=name, python: new_resource.name, virtualenv: nil)
190
+ def test_import(name, path=name, python: new_resource.name, virtualenv: nil, user: nil)
161
191
  # Only queue up this resource once, the ivar is just for tracking.
162
192
  @python_import_test ||= file ::File.join(new_resource.path, 'import_version.py') do
163
193
  user 'root'
@@ -175,6 +205,7 @@ EOH
175
205
 
176
206
  python_execute "#{@python_import_test.path} #{name} #{::File.join(new_resource.path, "import_#{path}")}" do
177
207
  python python if python
208
+ user user if user
178
209
  virtualenv virtualenv if virtualenv
179
210
  end
180
211
  end
@@ -112,7 +112,7 @@ module PoisePython
112
112
  private
113
113
 
114
114
  def install_python
115
- return if ::File.exist?(new_resource.path)
115
+ return if ::File.exist?(python_binary)
116
116
 
117
117
  cmd = python_shell_out(%w{-m venv -h})
118
118
  if cmd.error?
@@ -16,5 +16,5 @@
16
16
 
17
17
 
18
18
  module PoisePython
19
- VERSION = '1.1.2'
19
+ VERSION = '1.2.0'
20
20
  end
@@ -20,7 +20,7 @@ set :backend, :exec
20
20
  # Set up the shared example for python_runtime_test.
21
21
  RSpec.shared_examples 'a python_runtime_test' do |python_name, version=nil|
22
22
  let(:python_name) { python_name }
23
- let(:python_path) { File.join('', 'root', "python_test_#{python_name}") }
23
+ let(:python_path) { File.join('', 'opt', "python_test_#{python_name}") }
24
24
  # Helper for all the file checks.
25
25
  def self.assert_file(rel_path, should_exist=true, &block)
26
26
  describe rel_path do
@@ -24,6 +24,7 @@ describe PoisePython::PythonProviders::PortablePyPy3 do
24
24
  step_into(:python_runtime)
25
25
  recipe do
26
26
  python_runtime 'test' do
27
+ provider_no_auto 'dummy'
27
28
  version node['poise_python_version']
28
29
  virtualenv_version false
29
30
  end
@@ -24,6 +24,7 @@ describe PoisePython::PythonProviders::PortablePyPy do
24
24
  step_into(:python_runtime)
25
25
  recipe do
26
26
  python_runtime 'test' do
27
+ provider_no_auto 'dummy'
27
28
  version node['poise_python_version']
28
29
  virtualenv_version false
29
30
  end
@@ -37,7 +38,7 @@ describe PoisePython::PythonProviders::PortablePyPy do
37
38
 
38
39
  context 'with version pypy' do
39
40
  let(:python_version) { 'pypy' }
40
- it_behaves_like 'portablepypy provider', 'pypy-2.6'
41
+ it_behaves_like 'portablepypy provider', 'pypy-4.0.1'
41
42
  end # /context with version pypy
42
43
 
43
44
  context 'with version pypy-2.4' do
@@ -53,6 +54,6 @@ describe PoisePython::PythonProviders::PortablePyPy do
53
54
  end
54
55
  end
55
56
 
56
- it { is_expected.to uninstall_poise_languages_static('/opt/pypy-2.6') }
57
+ it { is_expected.to uninstall_poise_languages_static('/opt/pypy-4.0.1') }
57
58
  end # /context action :uninstall
58
59
  end
@@ -24,6 +24,7 @@ describe PoisePython::PythonProviders::Scl do
24
24
  step_into(:python_runtime)
25
25
  recipe do
26
26
  python_runtime 'test' do
27
+ provider_no_auto 'dummy'
27
28
  version node['poise_python_version']
28
29
  virtualenv_version false
29
30
  end
@@ -25,14 +25,15 @@ describe PoisePython::PythonProviders::System do
25
25
  step_into(:python_runtime)
26
26
  recipe do
27
27
  python_runtime 'test' do
28
+ provider_no_auto 'dummy'
28
29
  version node['poise_python_version']
29
30
  virtualenv_version false
30
31
  end
31
32
  end
32
33
 
33
- shared_examples_for 'system provider' do |candidates, pkg|
34
+ shared_examples_for 'system provider' do |candidates=nil, pkg|
34
35
  it { expect(python_runtime.provider_for_action(:install)).to be_a described_class }
35
- it { expect(system_package_candidates).to eq candidates }
36
+ it { expect(system_package_candidates).to eq candidates } if candidates
36
37
  it { is_expected.to install_poise_languages_system(pkg) }
37
38
  it do
38
39
  expect_any_instance_of(described_class).to receive(:install_system_packages)
@@ -64,6 +65,23 @@ describe PoisePython::PythonProviders::System do
64
65
  it_behaves_like 'system provider', %w{python2.3 python23 python}, 'python2.3'
65
66
  end # /context with version 2.3
66
67
 
68
+ context 'on Debian 8.1' do
69
+ before { chefspec_options.update(platform: 'debian', version: '8.1') }
70
+ it_behaves_like 'system provider', 'python3.4'
71
+ end # /context on Debian 8.1
72
+
73
+ context 'on CentOS 7' do
74
+ before { chefspec_options.update(platform: 'centos', version: '7.0') }
75
+ recipe do
76
+ python_runtime 'test' do
77
+ provider :system
78
+ version node['poise_python_version']
79
+ virtualenv_version false
80
+ end
81
+ end
82
+ it_behaves_like 'system provider', 'python'
83
+ end # /context on Debian 8.1
84
+
67
85
  context 'action :uninstall' do
68
86
  recipe do
69
87
  python_runtime 'test' do
@@ -17,19 +17,20 @@
17
17
  require 'spec_helper'
18
18
 
19
19
  describe PoisePython::Resources::PipRequirements do
20
- let(:pip_cmd) { }
20
+ let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/requirements.txt} }
21
21
  let(:pip_output) { '' }
22
+ let(:pip_user) { nil }
23
+ let(:pip_group) { nil }
22
24
  step_into(:pip_requirements)
23
25
  before do
24
26
  allow(File).to receive(:directory?).and_return(false)
25
27
  allow(File).to receive(:directory?).with('/test').and_return(true)
26
28
  end
27
29
  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))
30
+ expect_any_instance_of(PoisePython::Resources::PipRequirements::Provider).to receive(:python_shell_out!).with(pip_cmd, {user: pip_user, group: pip_group}).and_return(double(stdout: pip_output))
29
31
  end
30
32
 
31
33
  context 'with a directory' do
32
- let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/requirements.txt} }
33
34
  recipe do
34
35
  pip_requirements '/test'
35
36
  end
@@ -46,6 +47,28 @@ describe PoisePython::Resources::PipRequirements do
46
47
  it { is_expected.to install_pip_requirements('/test/reqs.txt') }
47
48
  end # /context with a file
48
49
 
50
+ context 'with a user' do
51
+ let(:pip_user) { 'testuser' }
52
+ recipe do
53
+ pip_requirements '/test' do
54
+ user 'testuser'
55
+ end
56
+ end
57
+
58
+ it { is_expected.to install_pip_requirements('/test') }
59
+ end # /context with a user
60
+
61
+ context 'with a group' do
62
+ let(:pip_group) { 'testgroup' }
63
+ recipe do
64
+ pip_requirements '/test' do
65
+ group 'testgroup'
66
+ end
67
+ end
68
+
69
+ it { is_expected.to install_pip_requirements('/test') }
70
+ end # /context with a group
71
+
49
72
  context 'action :upgrade' do
50
73
  let(:pip_cmd) { %w{-m pip.__main__ install --upgrade --requirement /test/requirements.txt} }
51
74
  recipe do
@@ -58,7 +81,6 @@ describe PoisePython::Resources::PipRequirements do
58
81
  end # /context action :upgrade
59
82
 
60
83
  context 'with output' do
61
- let(:pip_cmd) { %w{-m pip.__main__ install --requirement /test/requirements.txt} }
62
84
  let(:pip_output) { 'Successfully installed' }
63
85
  recipe do
64
86
  pip_requirements '/test'
@@ -21,7 +21,157 @@ describe PoisePython::Resources::PythonPackage do
21
21
  end # /describe PoisePython::Resources::PythonPackage::Resource
22
22
 
23
23
  describe PoisePython::Resources::PythonPackage::Provider do
24
- let(:test_provider) { described_class.new(nil, nil) }
24
+ let(:test_resource) { nil }
25
+ let(:test_provider) { described_class.new(test_resource, chef_run.run_context) }
26
+ def stub_cmd(cmd, **options)
27
+ output_options = {error?: options.delete(:error?) || false, stdout: options.delete(:stdout) || '', stderr: options.delete(:stderr) || ''}
28
+ expect(test_provider).to receive(:python_shell_out!).with(cmd, options).and_return(double("python #{cmd} return", **output_options))
29
+ end
30
+
31
+ describe '#load_current_resource' do
32
+ let(:package_name) { nil }
33
+ let(:package_version) { nil }
34
+ let(:test_resource) { PoisePython::Resources::PythonPackage::Resource.new(package_name, chef_run.run_context).tap {|r| r.version(package_version) if package_version } }
35
+ let(:candidate_version) { subject; test_provider.candidate_version }
36
+ subject { test_provider.load_current_resource }
37
+
38
+ context 'with package_name foo' do
39
+ let(:package_name) { 'foo' }
40
+ before do
41
+ stub_cmd(%w{-m pip.__main__ list}, stdout: '')
42
+ stub_cmd(%w{- list --outdated foo}, input: kind_of(String), stdout: "foo (Current: 0 Latest: 1.0.0 [wheel])\n")
43
+ end
44
+
45
+ its(:version) { is_expected.to be nil }
46
+ it { expect(candidate_version).to eq '1.0.0' }
47
+ end # /context with package_name foo
48
+
49
+ context 'with package_name ["foo", "bar"]' do
50
+ let(:package_name) { %w{foo bar} }
51
+ before do
52
+ stub_cmd(%w{-m pip.__main__ list}, stdout: '')
53
+ stub_cmd(%w{- list --outdated foo bar}, input: kind_of(String), stdout: "foo (Current: 0 Latest: 1.0.0 [wheel])\nbar (Current: 0 Latest: 2.0.0 [wheel])\n")
54
+ end
55
+
56
+ its(:version) { is_expected.to eq [nil, nil] }
57
+ it { expect(candidate_version).to eq %w{1.0.0 2.0.0} }
58
+ end # /context with package_name ["foo", "bar"]
59
+
60
+ context 'with a package with extras' do
61
+ let(:package_name) { 'foo[bar]' }
62
+ before do
63
+ stub_cmd(%w{-m pip.__main__ list}, stdout: '')
64
+ stub_cmd(%w{- list --outdated foo}, input: kind_of(String), stdout: "foo (Current: 0 Latest: 1.0.0 [wheel])\n")
65
+ end
66
+
67
+ its(:version) { is_expected.to be nil }
68
+ it { expect(candidate_version).to eq '1.0.0' }
69
+ end # /context with a package with extras
70
+ end # /describe #load_current_resource
71
+
72
+ describe 'actions' do
73
+ let(:package_name) { nil }
74
+ let(:current_version) { nil }
75
+ let(:candidate_version) { nil }
76
+ let(:test_resource) { PoisePython::Resources::PythonPackage::Resource.new(package_name, chef_run.run_context) }
77
+ subject { test_provider.run_action }
78
+ before do
79
+ current_version = self.current_version
80
+ candidate_version = self.candidate_version
81
+ allow(test_provider).to receive(:load_current_resource) do
82
+ current_resource = double('current_resource', package_name: package_name, version: current_version)
83
+ test_provider.instance_eval do
84
+ @current_resource = current_resource
85
+ @candidate_version = candidate_version
86
+ end
87
+ end
88
+ end
89
+
90
+ describe 'action :install' do
91
+ before { test_provider.action = :install }
92
+
93
+ context 'with package_name foo' do
94
+ let(:package_name) { 'foo' }
95
+ let(:candidate_version) { '1.0.0' }
96
+ it do
97
+ stub_cmd(%w{-m pip.__main__ install foo==1.0.0})
98
+ subject
99
+ end
100
+ end # /context with package_name foo
101
+
102
+ context 'with package_name ["foo", "bar"]' do
103
+ let(:package_name) { %w{foo bar} }
104
+ let(:candidate_version) { %w{1.0.0 2.0.0} }
105
+ it do
106
+ stub_cmd(%w{-m pip.__main__ install foo==1.0.0 bar==2.0.0})
107
+ subject
108
+ end
109
+ end # /context with package_name ["foo", "bar"]
110
+
111
+ context 'with options' do
112
+ let(:package_name) { 'foo' }
113
+ let(:candidate_version) { '1.0.0' }
114
+ before { test_resource.options('--editable') }
115
+ it do
116
+ stub_cmd('-m pip.__main__ install --editable foo\\=\\=1.0.0')
117
+ subject
118
+ end
119
+ end # /context with options
120
+
121
+ context 'with a package with extras' do
122
+ let(:package_name) { 'foo[bar]' }
123
+ let(:candidate_version) { '1.0.0' }
124
+ it do
125
+ stub_cmd(%w{-m pip.__main__ install foo[bar]==1.0.0})
126
+ subject
127
+ end
128
+ end # /context with a package with extras
129
+ end # /describe action :install
130
+
131
+ describe 'action :upgrade' do
132
+ before { test_provider.action = :upgrade }
133
+
134
+ context 'with package_name foo' do
135
+ let(:package_name) { 'foo' }
136
+ let(:candidate_version) { '1.0.0' }
137
+ it do
138
+ stub_cmd(%w{-m pip.__main__ install --upgrade foo==1.0.0})
139
+ subject
140
+ end
141
+ end # /context with package_name foo
142
+
143
+ context 'with package_name ["foo", "bar"]' do
144
+ let(:package_name) { %w{foo bar} }
145
+ let(:candidate_version) { %w{1.0.0 2.0.0} }
146
+ it do
147
+ stub_cmd(%w{-m pip.__main__ install --upgrade foo==1.0.0 bar==2.0.0})
148
+ subject
149
+ end
150
+ end # /context with package_name ["foo", "bar"]
151
+ end # /describe action :upgrade
152
+
153
+ describe 'action :remove' do
154
+ before { test_provider.action = :remove }
155
+
156
+ context 'with package_name foo' do
157
+ let(:package_name) { 'foo' }
158
+ let(:current_version) { '1.0.0' }
159
+ it do
160
+ stub_cmd(%w{-m pip.__main__ uninstall --yes foo})
161
+ subject
162
+ end
163
+ end # /context with package_name foo
164
+
165
+ context 'with package_name ["foo", "bar"]' do
166
+ let(:package_name) { %w{foo bar} }
167
+ let(:current_version) { %w{1.0.0 2.0.0} }
168
+ it do
169
+ stub_cmd(%w{-m pip.__main__ uninstall --yes foo bar})
170
+ subject
171
+ end
172
+ end # /context with package_name ["foo", "bar"]
173
+ end # /describe action :remove
174
+ end # /describe actions
25
175
 
26
176
  describe '#parse_pip_outdated' do
27
177
  let(:text) { '' }
@@ -41,6 +191,17 @@ Fabric (Current: 1.9.1 Latest: 1.10.2 [wheel])
41
191
  EOH
42
192
  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
193
  end # /context with standard content
194
+
195
+ context 'with malformed content' do
196
+ let(:text) { <<-EOH }
197
+ boto (Current: 2.25.0 Latest: 2.38.0 [wheel])
198
+ botocore (Current: 0.56.0 Latest: 1.1.1 [wheel])
199
+ certifi (Current: 14.5.14 Latest: 2015.4.28 [wheel])
200
+ cffi (Current: 0.8.1 Latest: 1.1.2 [sdist])
201
+ Fabric (Future: 1.9.1 [wheel])
202
+ EOH
203
+ it { is_expected.to eq({'boto' => '2.38.0', 'botocore' => '1.1.1', 'certifi' => '2015.4.28', 'cffi' => '1.1.2'}) }
204
+ end # /context with malformed content
44
205
  end # /describe #parse_pip_outdated
45
206
 
46
207
  describe '#parse_pip_list' do
@@ -60,6 +221,16 @@ flake8 (2.1.0.dev0)
60
221
  EOH
61
222
  it { is_expected.to eq({'eventlet' => '0.12.1', 'fabric' => '1.9.1', 'fabric-rundeck' => '1.2', 'flake8' => '2.1.0.dev0'}) }
62
223
  end # /context with standard content
224
+
225
+ context 'with malformed content' do
226
+ let(:text) { <<-EOH }
227
+ eventlet (0.12.1)
228
+ Fabric (1.9.1)
229
+ fabric-rundeck (1.2, /Users/coderanger/src/bal/fabric-rundeck)
230
+ flake 8 (2.1.0.dev0)
231
+ EOH
232
+ it { is_expected.to eq({'eventlet' => '0.12.1', 'fabric' => '1.9.1', 'fabric-rundeck' => '1.2'}) }
233
+ end # /context with malformed content
63
234
  end # /describe #parse_pip_list
64
235
  end # /describe PoisePython::Resources::PythonPackage::Provider
65
236
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: poise-python
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Kantrowitz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-22 00:00:00.000000000 Z
11
+ date: 2016-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: halite