poise-python 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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