squared 0.5.20 → 0.6.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.
@@ -8,37 +8,52 @@ module Squared
8
8
  DIR_PYTHON = (DEP_PYTHON + %w[README.rst]).freeze
9
9
  OPT_PYTHON = {
10
10
  common: %w[b B d E h i I O P q s S u v x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
11
- build: %w[C=bm n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel config-setting=q installer=b
11
+ build: %w[n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel C|config-setting=q installer=b
12
12
  o|outdir=p].freeze,
13
13
  venv: %w[clear copies symlinks system-site-packages upgrade upgrade-deps without-scm-ignore-files without-pip
14
14
  prompt=q].freeze
15
15
  }.freeze
16
16
  OPT_PIP = {
17
- common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input no-python-version-warning
18
- q|quiet require-virtualenv v|verbose cache-dir=p cert=p client-cert=p exists-action=b log=p proxy=q
19
- python=q retries=i timeout=i trusted-host=b use-deprecated=b use-feature=b].freeze,
20
- install: %w[break-system-packages check-build-dependencies compile dry-run force-reinstall I|ignore-installed
21
- ignore-requires-python no-build-isolation no-clean no-compile no-deps no-index no-warn-conflicts
22
- no-warn-script-location pre prefer-binary require-hashes U|upgrade use-pep517 user abi=b
23
- config-settings=q c|constraint=p e|editable=v extra-index-url=q f|find-links=q global-option=q
24
- implementation=b i|index-url=q no-binary=q only-binary=q platform=q prefix=p progress-bar=b
25
- python-version=q report=p r|requirement=p root=p root-user-action=b src=p t|target=p
26
- upgrade-strategy=b].freeze,
17
+ common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input q|quiet require-virtualenv
18
+ v|verbose cache-dir=p cert=p client-cert=p exists-action=b keyring-provider=b log=p proxy=q
19
+ python=q resume-retries=i retries=i timeout=i trusted-host=b use-deprecated=b
20
+ use-feature=b].freeze,
21
+ cache: %w[format=b].freeze,
22
+ completion: %w[b|bash f|fish p|powershell z|zsh].freeze,
23
+ config: %w[global user site editor=p].freeze,
24
+ debug: %w[abi=b implementation=b platform=b python-version=b].freeze,
25
+ download: %w[d|dest=p].freeze,
26
+ freeze: %w[all exclude-editable l|local user exclude=b path=p r|requirement=p].freeze,
27
+ index: %w[json].freeze,
28
+ inspect: %w[local user path=p].freeze,
29
+ install: %w[break-system-packages compile dry-run force-reinstall I|ignore-installed no-compile
30
+ no-warn-conflicts no-warn-script-location U|upgrade user prefix=p report=p root=p
31
+ root-user-action=b t|target=p upgrade-strategy=b].freeze,
32
+ install_a: %w[ignore-requires-python no-index pre extra-index-url=q f|find-links=q i|index-url=q no-binary=q
33
+ only-binary=q].freeze,
34
+ install_b: %w[check-build-dependencies no-build-isolation no-clean no-deps prefer-binary require-hashes
35
+ use-pep517 c|constraint=p group=q progress-bar=b r|requirement=p src=p].freeze,
36
+ install_c: %w[C|config-settings=q e|editable=v].freeze,
37
+ hash: %w[a|algorithm].freeze,
38
+ list: %w[e|editable exclude-editable include-editable l|local no-index not-required o|outdated pre u|uptodate
39
+ user exclude=b extra-index-url=q format=b f|find-links=q i|index-url=q path=p].freeze,
40
+ lock: %w[o|output=p].freeze,
41
+ show: %w[f|files].freeze,
27
42
  uninstall: %w[break-system-packages y|yes r|requirement=p root-user-action=b].freeze,
28
- freeze: %w[all exclude-editable l|local user exclude=b path=p r|requirement=p].freeze
43
+ wheel: %w[no-verify w|wheel-dir=p].freeze
29
44
  }.freeze
30
45
  OPT_POETRY = {
31
46
  common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose P|project=p].freeze,
32
47
  build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
33
- publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=q r|repository=q
34
- u|username=qq].freeze
48
+ publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=b r|repository=q
49
+ u|username=b].freeze
35
50
  }.freeze
36
51
  OPT_PDM = {
37
52
  common: %w[I|ignore-python no-cache n|non-interactive].freeze,
38
53
  build: %w[C=bm no-clean no-isolation no-sdist no-wheel quiet verbose config-setting=q d|dest=p p|project=p
39
54
  k|skip=b].freeze,
40
55
  publish: %w[no-build no-very-ssl quiet S|sign skip-existing verbose ca-certs=p c|comment=q d|dest=p
41
- i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=qq].freeze
56
+ i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=b].freeze
42
57
  }.freeze
43
58
  OPT_HATCH = {
44
59
  common: %w[color interactive no-color no-interactive cache-dir=p config=p data-dir=p e|env=b p|project=b
@@ -50,13 +65,20 @@ module Squared
50
65
  OPT_TWINE = {
51
66
  publish: %w[attestations disable-progress-bar non-interactive s|sign skip-existing verbose cert=p
52
67
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
53
- sign-with=b u|username=qq].freeze
68
+ sign-with=b u|username=q].freeze
54
69
  }.freeze
55
- private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE
70
+ PASS_PYTHON = {
71
+ pip: {
72
+ debug: %w[platform].freeze,
73
+ install: %w[C config-settings c constraint extra-index-url no-binary only-binary platform
74
+ r requirement].freeze,
75
+ list: %w[exclude extra-index-url].freeze
76
+ }.freeze
77
+ }.freeze
78
+ private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE,
79
+ :PASS_PYTHON
56
80
 
57
81
  class << self
58
- def populate(*); end
59
-
60
82
  def tasks
61
83
  [:outdated].freeze
62
84
  end
@@ -78,7 +100,7 @@ module Squared
78
100
 
79
101
  attr_reader :venv, :editable
80
102
 
81
- def initialize(*, editable: '.', asdf: 'python', **kwargs)
103
+ def initialize(*, editable: '.', verbose: nil, asdf: 'python', **kwargs)
82
104
  super
83
105
  if @pass.include?(Python.ref)
84
106
  initialize_ref Python.ref
@@ -94,10 +116,10 @@ module Squared
94
116
 
95
117
  subtasks({
96
118
  'venv' => %i[exec create remove show].freeze,
97
- 'pip' => %i[upgrade uninstall freeze].freeze,
119
+ 'pip' => %i[upgrade uninstall wheel reinstall freeze].freeze,
98
120
  'install' => %i[user force upgrade target editable].freeze,
99
121
  'outdated' => %i[major minor patch].freeze,
100
- 'build' => %i[python poetry pdm hatch].freeze,
122
+ 'build' => %i[poetry pdm hatch python].freeze,
101
123
  'publish' => %i[poetry pdm hatch twine].freeze,
102
124
  'run' => nil,
103
125
  'exec' => nil
@@ -132,7 +154,7 @@ module Squared
132
154
  format_desc action, nil, "script+|#{indexchar}index+|#,pattern*"
133
155
  task action, [:command] do |_, args|
134
156
  found = 0
135
- %w[tool.poetry.scripts tool.pdm.scripts project.scripts].each_with_index do |table, index|
157
+ %w[tool.poetry.scripts tool.pdm.scripts project.scripts].each_with_index do |table, i|
136
158
  next if (list = read_pyproject(table)).empty?
137
159
 
138
160
  if args.command == '#'
@@ -141,8 +163,8 @@ module Squared
141
163
  else
142
164
  args.to_a.each do |val|
143
165
  if (n, = indexitem(val))
144
- if (script, = list[n - 1])
145
- case index
166
+ if (script, = list[n.pred])
167
+ case i
146
168
  when 0
147
169
  script = session_output 'poetry', 'run', script
148
170
  when 1
@@ -159,7 +181,7 @@ module Squared
159
181
  log.warn "run script #{n} of #{list.size} (out of range)"
160
182
  end
161
183
  else
162
- case index
184
+ case i
163
185
  when 0
164
186
  found |= 1
165
187
  run(session_output('poetry', 'run', val), from: :run)
@@ -183,24 +205,21 @@ module Squared
183
205
  end
184
206
  end
185
207
  when 'exec'
186
- format_desc action, nil, 'command|:,args*'
208
+ format_desc action, nil, ':|command,args*'
187
209
  task action do |_, args|
188
- i = (args = args.to_a).delete(':')
189
- cmd = if i && !workspace.windows?
190
- readline('Enter script', force: true, multiline: ['##', ';'])
210
+ args = args.to_a
211
+ cmd = if (i = args.delete(':')) && !workspace.windows?
212
+ readline('Enter script', force: true, multiline: %w[## ;])
191
213
  elsif i || args.empty?
192
214
  readline('Enter command', force: true)
193
215
  else
194
- if (val = command_args(args, min: 1, prefix: 'python'))
195
- args << val
196
- end
197
- args.join(' ')
216
+ (args << command_args(args, min: 1, prefix: 'python')).compact.join(' ')
198
217
  end
199
218
  shell(cmd, name: :exec, chdir: path)
200
219
  end
201
220
  end
202
221
  else
203
- namespace action do
222
+ namespace action do |ns|
204
223
  flags.each do |flag|
205
224
  case action
206
225
  when 'venv'
@@ -215,11 +234,11 @@ module Squared
215
234
  when :remove
216
235
  next unless projectpath?(venv)
217
236
 
218
- format_desc action, flag, 'c|create?,d|depend?'
237
+ format_desc action, flag, 'c/reate?,d/epend?'
219
238
  task flag do |_, args|
220
239
  rm_rf(venv, verbose: true)
221
- venv_init if has_value?(%w[c create], args.to_a)
222
- depend if has_value?(%w[d depend], args.to_a)
240
+ venv_init if has_value?(args = args.to_a, 'c', 'create')
241
+ depend if has_value?(args, 'd', 'depend')
223
242
  end
224
243
  when :exec
225
244
  format_desc action, flag, 'command,args*'
@@ -227,7 +246,7 @@ module Squared
227
246
  args = args.to_a
228
247
  if args.empty?
229
248
  args = readline('Enter command', force: true).split(' ', 2)
230
- elsif args.size == 1 && !option('interactive', prefix: 'venv', equals: '0')
249
+ elsif args.size == 1 && !option('interactive', equals: '0', prefix: ref)
231
250
  args << readline('Enter arguments', force: false)
232
251
  end
233
252
  venv_init
@@ -250,21 +269,35 @@ module Squared
250
269
  when :freeze
251
270
  format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
252
271
  task flag do |_, args|
253
- if (file = pip(flag, args.to_a)) && verbose
272
+ if (file = pip(flag, opts: args.to_a, banner: true)) && verbose
254
273
  puts File.read(file)
255
274
  end
256
275
  end
257
276
  when :uninstall
258
277
  format_desc action, flag, 'package+,opts*'
259
278
  task flag do |_, args|
260
- pip flag, args.to_a
279
+ pip(flag, opts: args.to_a, banner: true)
280
+ end
281
+ when :wheel
282
+ next unless pyprojectfile || setuptools?
283
+
284
+ format_desc action, flag, 'opts*,args*'
285
+ task flag do |_, args|
286
+ pip(flag, opts: args.to_a, banner: true)
287
+ end
288
+ when :reinstall
289
+ if venv && projectpath?(venv)
290
+ format_desc action, flag
291
+ task flag do
292
+ ns['venv:remove'].invoke('depend')
293
+ end
261
294
  end
262
295
  end
263
296
  when 'install'
264
297
  format_desc(action, flag, 'opts*', before: case flag
265
298
  when :target then 'dir'
266
- when :editable then 'path/url?'
267
- when :upgrade then 'strategy?,package+'
299
+ when :editable then 'path/url?,opts*'
300
+ when :upgrade then 'strategy?,opts*,package+'
268
301
  end)
269
302
  case flag
270
303
  when :editable
@@ -273,17 +306,18 @@ module Squared
273
306
  end
274
307
  when :upgrade
275
308
  task flag, [:strategy] do |_, args|
276
- args = case (strategy = args.strategy)
277
- when 'eager', 'only-if-needed'
278
- args.extras
279
- when 'needed'
280
- strategy = 'only-if-needed'
281
- args.extras
282
- else
283
- strategy = nil
284
- args.to_a
285
- end
286
- install(flag, args, strategy: strategy)
309
+ install flag, (case args.strategy
310
+ when 'eager'
311
+ 'eager'
312
+ when /(?:\Aonly-if|needed\z)/
313
+ 'only-if-needed'
314
+ end.yield_self do |val|
315
+ if val
316
+ args.extras << "upgrade-strategy=#{val}"
317
+ else
318
+ args.to_a
319
+ end
320
+ end)
287
321
  end
288
322
  when :target
289
323
  task flag, [:dir] do |_, args|
@@ -296,30 +330,26 @@ module Squared
296
330
  end
297
331
  end
298
332
  when 'outdated'
299
- format_desc action, flag, 'eager?,user?'
333
+ format_desc action, flag, 'eager?,user?,s/elect'
300
334
  task flag do |_, args|
301
335
  outdated flag, args.to_a
302
336
  end
303
337
  when 'build'
304
- case flag
305
- when :poetry
306
- next unless build_backend == 'poetry.core.masonry.api'
307
- when :pdm
308
- next unless build_backend == 'pdm.backend'
309
- when :hatch
310
- next unless build_backend == 'hatchling.build'
311
- end
338
+ next if (be = backend?(flag)) == false
339
+
312
340
  format_desc(action, flag, 'opts*', after: case flag
313
341
  when :poetry then 'output?'
314
342
  when :pdm then 'dest?'
315
343
  when :hatch then 'location?'
316
- else 'outdir?'
344
+ else 'srcdir?'
317
345
  end)
318
346
  task flag do |_, args|
319
347
  build! flag, args.to_a
320
348
  end
321
- break unless flag == :python
349
+ break if be
322
350
  when 'publish'
351
+ next if (be = backend?(flag)) == false
352
+
323
353
  format_desc(action, flag, 'test?,opts*', after: case flag
324
354
  when :hatch then 'artifacts?'
325
355
  when :twine then 'dist?'
@@ -333,6 +363,7 @@ module Squared
333
363
  false
334
364
  end)
335
365
  end
366
+ break if be
336
367
  end
337
368
  end
338
369
  end
@@ -352,7 +383,6 @@ module Squared
352
383
  cmd << '--no-root' if option('no-root')
353
384
  else
354
385
  cmd = pip_session 'install'
355
- cmd << '--upgrade-strategy=eager' if env('PYTHON_UPDATE')
356
386
  if flag
357
387
  case flag
358
388
  when :user
@@ -362,11 +392,12 @@ module Squared
362
392
  when :force
363
393
  cmd << '--force-reinstall'
364
394
  end
365
- append_pip(flag, opts, from: :install)
395
+ op = append_pip(flag, opts, from: :install)
396
+ op.clear
366
397
  else
367
398
  append_global
368
399
  end
369
- cmd << "-r #{DEP_PYTHON[4]}" if basepath(DEP_PYTHON[4]).exist? && !session_arg?('r', 'requirement')
400
+ cmd << "-r #{DEP_PYTHON[4]}" if exist?(DEP_PYTHON[4]) && !session_arg?('r', 'requirement')
370
401
  append_editable
371
402
  end
372
403
  run(from: :depend, sync: sync)
@@ -375,18 +406,24 @@ module Squared
375
406
 
376
407
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
377
408
  cmd = pip_session 'list --outdated'
409
+ cmd << '--not-required' unless flag || option('not-required', equals: '0')
410
+ cmd << '--local' if option('l', 'local')
378
411
  append_global
379
412
  cmd = session_done cmd
380
413
  log.info cmd
381
414
  on :first, :outdated
382
415
  banner = format_banner cmd
383
- print_item banner if sync
384
- start = 0
385
- found = 0
386
- major = []
387
- minor = []
388
- patch = []
416
+ items = if sync
417
+ print_item banner
418
+ [] if flag && has_value?(opts, 's', 'select') && !stdin?
419
+ end
389
420
  pwd_set(from: :outdated) do
421
+ tc = theme[:current]
422
+ start = 0
423
+ found = 0
424
+ major = []
425
+ minor = []
426
+ patch = []
390
427
  buffer = []
391
428
  out = ->(val) { sync ? puts(val) : buffer << val }
392
429
  if workspace.windows?
@@ -420,26 +457,27 @@ module Squared
420
457
  styles = color(:yellow)
421
458
  else
422
459
  styles = color(:green)
423
- line = sub_style(line, pat: /^(\S+)(.+)$/, styles: if type == 2
424
- styles << :bold
425
- theme[:major]
426
- else
427
- theme[:active]
428
- end)
460
+ line = sub_style(line, styles: if type == 2
461
+ styles += [:bold]
462
+ theme[:major]
463
+ else
464
+ theme[:active]
465
+ end, pat: /^(\S+)(.+)$/)
429
466
  end
430
- if theme[:current]
431
- line = sub_style(line, pat: /^(.+)(#{Regexp.escape(current)})(.+)$/, styles: theme[:current],
432
- index: 2)
433
- end
434
- line = sub_style(line, pat: /^(.+)(#{Regexp.escape(latest)})(.+)$/, styles: styles, index: 2)
467
+ line = sub_style(line, **opt_style(tc, /^(.+)(#{Regexp.escape(current)})(.+)$/, 2)) if tc
468
+ line = sub_style(line, **opt_style(styles, /^(.+)(#{Regexp.escape(latest)})(.+)$/, 2))
435
469
  found += 1
436
470
  end
437
- out.call("#{start.to_s.rjust(2)}. #{line}")
471
+ if items
472
+ items << [line, name]
473
+ else
474
+ out.call("#{start.to_s.rjust(2)}. #{line}")
475
+ end
438
476
  start += 1
439
477
  elsif line.start_with?('Package')
440
478
  unless stdin?
441
- sub = { pat: /^(.*)(?<!\dm)(Package|Latest)(.+)$/, styles: theme[:header], index: 2 }
442
- out.call(print_footer(" # #{line.chomp}", reverse: true, sub: [sub, sub]))
479
+ sub = [opt_style(theme[:header], /^(.*)(?<!\dm)(Package|Latest)(.+)$/, 2)] * 2
480
+ out.call(print_footer(" # #{line.chomp}", reverse: true, sub: sub))
443
481
  end
444
482
  start += 1
445
483
  end
@@ -449,17 +487,23 @@ module Squared
449
487
  puts buffer
450
488
  end
451
489
  if found > 0
452
- print_status(major.size, minor.size, patch.size, from: :outdated)
453
- pkg = case flag
454
- when :major
455
- major + minor + patch
456
- when :minor
457
- minor + patch
458
- when :patch
459
- patch
460
- end
461
- unless !pkg || pkg.empty?
462
- install(:upgrade, pkg, strategy: opts.include?('eager') ? 'eager' : nil, user: opts.include?('user'))
490
+ if items
491
+ choice('Select a package', items.map(&:first),
492
+ multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
493
+ else
494
+ case flag
495
+ when :major
496
+ major + minor + patch
497
+ when :minor
498
+ minor + patch
499
+ else
500
+ patch
501
+ end
502
+ end.tap do |packages|
503
+ unless packages.empty?
504
+ install(:upgrade, opts.grep(/^(?:eager|user)$/), packages: packages, banner: false)
505
+ end
506
+ print_status(major.size, minor.size, patch.size, from: :outdated)
463
507
  end
464
508
  elsif start == 0
465
509
  puts 'No updates were found'
@@ -468,83 +512,95 @@ module Squared
468
512
  on :last, :outdated
469
513
  end
470
514
 
471
- def install(flag, opts = [], strategy: nil, user: nil)
472
- cmd = pip_session 'install'
473
- out = append_pip(flag, opts, from: :install)
515
+ def install(flag, opts = [], packages: [], banner: true)
516
+ op = append_pip(flag, opts, target: pip_session('install'), from: :install)
474
517
  case flag
475
518
  when :editable
476
- cmd << quote_option('e', out.pop || editable || '.')
477
- option_clear out
519
+ op << quote_option('e', op.pop || editable || '.')
520
+ op.clear
478
521
  when :upgrade
479
- raise_error('no packages listed', hint: flag) if out.empty?
480
- cmd << '--upgrade'
481
- cmd << '--user' if user
482
- cmd << basic_option('upgrade-strategy', strategy) if strategy
483
- append_value out
484
- python_session('-m pip', *cmd.to_a.drop(1)) if workspace.windows?
522
+ op.concat(packages)
523
+ raise_error 'no packages listed', hint: flag if op.empty?
524
+ op << '--upgrade'
525
+ op.append
526
+ python_session('-m pip', *op.to_a.drop(1)) if workspace.windows?
485
527
  end
486
- run(from: :install)
528
+ run(banner: banner, from: :install)
487
529
  end
488
530
 
489
531
  def build!(flag, opts = [])
490
- case flag
491
- when :poetry
492
- cmd = poetry_session 'build'
493
- list = OPT_POETRY[:build] + OPT_POETRY[:common]
494
- when :pdm
495
- cmd, opts = pdm_session('build', opts: opts)
496
- list = OPT_PDM[:build]
497
- when :hatch
498
- cmd, opts = hatch_session('build', opts: opts)
499
- list = OPT_HATCH[:build]
500
- else
501
- cmd, opts = python_session('-m build', opts: opts)
502
- list = OPT_PYTHON[:build]
503
- end
532
+ list = case flag
533
+ when :poetry
534
+ cmd = poetry_session 'build'
535
+ OPT_POETRY[:build] + OPT_POETRY[:common]
536
+ when :pdm
537
+ cmd, opts = pdm_session('build', opts: opts)
538
+ OPT_PDM[:build]
539
+ when :hatch
540
+ cmd, opts = hatch_session('build', opts: opts)
541
+ OPT_HATCH[:build]
542
+ else
543
+ cmd, opts = python_session('-m build', opts: opts)
544
+ OPT_PYTHON[:build]
545
+ end
546
+ srcdir = nil
504
547
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
548
+ op.each do |opt|
549
+ if !srcdir && exist?(opt.chomp('*')) && projectpath?(opt.chomp('*'))
550
+ srcdir = opt
551
+ else
552
+ op.found << opt
553
+ end
554
+ end
555
+ op.swap
505
556
  case flag
506
- when :hatch
507
- if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
508
- op.add_path(outdir)
557
+ when :poetry, :pdm
558
+ if srcdir
559
+ args = flag == :pdm ? %w[d dest] : %w[o output]
560
+ if op.arg?(*args)
561
+ op.push(srcdir)
562
+ else
563
+ op << quote_option(args.last, basepath(srcdir))
564
+ end
565
+ srcdir = nil
509
566
  end
510
- else
511
- unless op.empty?
512
- args = case flag
513
- when :poetry
514
- %w[o output]
515
- when :pdm
516
- %w[d dest]
517
- else
518
- srcdir = true
519
- %w[o outdir]
520
- end
521
- op << quote_option(args.last, basepath(op.shift)) unless op.arg?(*args)
567
+ when :hatch
568
+ if ENV['HATCH_BUILD_LOCATION']
569
+ srcdir = nil
570
+ else
571
+ srcdir ||= path
522
572
  end
573
+ op << basic_option('p', project) unless ENV['HATCH_PROJECT'] || op.arg?('p', 'project')
523
574
  end
524
- op.exist?(add: true, first: true) if srcdir
575
+ op.add_path(srcdir) if srcdir
525
576
  op.clear
526
577
  run(from: :"#{flag}:build")
527
578
  end
528
579
 
529
580
  def publish(flag, opts = [], test: false)
530
- case flag
531
- when :poetry
532
- poetry_session 'publish'
533
- list = OPT_POETRY[:publish] + OPT_POETRY[:common]
534
- when :pdm
535
- opts = pdm_session('publish', opts: opts).last
536
- list = OPT_PDM[:publish]
537
- when :hatch
538
- opts = hatch_session('publish', opts: opts).last
539
- list = OPT_HATCH[:publish]
540
- when :twine
541
- session 'twine', 'upload'
542
- list = OPT_TWINE[:publish]
543
- end
581
+ list = case flag
582
+ when :poetry
583
+ poetry_session 'publish'
584
+ OPT_POETRY[:publish] + OPT_POETRY[:common]
585
+ when :pdm
586
+ opts = pdm_session('publish', opts: opts).last
587
+ OPT_PDM[:publish]
588
+ when :hatch
589
+ opts = hatch_session('publish', opts: opts).last
590
+ OPT_HATCH[:publish]
591
+ else
592
+ session 'twine', 'upload'
593
+ OPT_TWINE[:publish]
594
+ end
544
595
  op = OptionPartition.new(opts, list, @session, project: self, single: singleopt(flag))
545
596
  dist = lambda do
546
- basepath('dist').tap do |dir|
547
- raise_error('no source files found', hint: dir) unless dir.directory? && !dir.empty?
597
+ dir = basepath 'dist'
598
+ return dir if dir.directory? && !dir.empty?
599
+
600
+ if dir.exist?
601
+ raise_error 'no source to publish', hint: dir
602
+ else
603
+ raise_error Errno::ENOENT, dir, hint: 'publish'
548
604
  end
549
605
  end
550
606
  if test
@@ -555,35 +611,93 @@ module Squared
555
611
  end
556
612
  end
557
613
  case flag
558
- when :hatch, :twine
614
+ when :poetry, :pdm
615
+ dist.call unless op.arg?(*(flag == :poetry ? ['dist-dir'] : %w[d dest]))
616
+ op.clear(pass: false)
617
+ else
559
618
  if op.empty?
560
- op.push("#{dist.call}/*")
619
+ op << "#{dist.call}/*"
561
620
  else
562
- op.map! { |val| basepath(val) }
621
+ op.add_path
563
622
  end
564
- op.append
565
- else
566
- dist.call unless op.arg?(*(flag == :poetry ? ['dist-dir'] : ['d', 'dest']))
567
- op.clear(pass: false)
568
623
  end
569
- run(from: :"#{flag}:publish", interactive: "Publish #{sub_style(project, styles: theme[:active])}")
624
+ run(from: :"#{flag}:publish", interactive: ['Publish', 'N', project])
570
625
  end
571
626
 
572
- def pip(flag, opts = [])
573
- cmd = pip_session flag
574
- out = append_pip(nil, opts, from: flag)
627
+ def pip(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
628
+ flag = flag.to_sym
629
+ pass = PASS_PYTHON[:pip].fetch(pip_install?(flag) ? :install : flag, []) + %w[v verbose] if pass.nil?
630
+ opts = session_opts(with, args: args, kwargs: kwargs, pass: pass)
575
631
  case flag
576
- when :uninstall
577
- raise_error('no packages listed', hint: flag) if out.empty?
578
- cmd.merge(out)
632
+ when :freeze, :inspect, :list, :check, :completion, :debug
633
+ opts.concat(args)
634
+ end
635
+ op = append_pip(flag, opts, target: pip_session(flag), from: flag)
636
+ case flag
637
+ when :install, :uninstall, :show, :index
638
+ op.concat(args)
639
+ if op.empty?
640
+ case flag
641
+ when :install, :uninstall
642
+ op << '.' if installable? && !op.arg?('r', 'requirement')
643
+ else
644
+ raise_error 'no packages listed', hint: flag
645
+ end
646
+ elsif flag == :install
647
+ op.append_any
648
+ elsif flag == :index
649
+ op.adjoin('versions', with: 'index')
650
+ .add_first
651
+ .clear
652
+ else
653
+ op.append
654
+ end
579
655
  when :freeze
580
656
  venv_init
581
- ret = basepath(out.shift || DEP_PYTHON[4])
582
- cmd << '>' << shell_quote(ret)
583
- option_clear out
657
+ op << '>'
658
+ op.add_quote(ret = basepath(op.detect { |val| op.exist?(val) } || DEP_PYTHON[4]))
659
+ .clear
660
+ when :cache
661
+ op.concat(args)
662
+ raise_error 'no subcommand', hint: flag if op.empty?
663
+ op << (action = op.shift)
664
+ case action
665
+ when 'dir', 'info', 'purge'
666
+ nil
667
+ when 'list', 'remove'
668
+ op.add_first(quote: true)
669
+ else
670
+ raise_error ArgumentError, "unrecognized args: #{action}", hint: flag
671
+ end
672
+ op.clear
673
+ when :config
674
+ op.concat(args)
675
+ raise_error 'no subcommand', hint: flag if op.empty?
676
+ op << (action = op.shift)
677
+ case action
678
+ when 'list', 'edit', 'debug'
679
+ nil
680
+ when 'get', 'unset', 'set'
681
+ op.add_first
682
+ op.add_first(quote: true, expect: true) if action == 'set'
683
+ else
684
+ raise_error ArgumentError, "unrecognized args: #{action}", hint: flag
685
+ end
686
+ op.clear
687
+ when :hash
688
+ op.append(projectmap(op.concat(args), parent: true))
689
+ when :wheel, :lock, :download
690
+ op.concat(args)
691
+ if !op.empty?
692
+ op.append
693
+ elsif installable? && !op.arg?('r', 'requirement')
694
+ op << '.'
695
+ end
696
+ else
697
+ op.clear
584
698
  end
585
- run(from: :"pip:#{flag}")
586
- ret
699
+ print_run(op, banner, **kwargs)
700
+ run(sync: sync, banner: banner, from: :"pip:#{flag}").yield_self { |val| ret || val }
587
701
  end
588
702
 
589
703
  def variable_set(key, *val, **, &blk)
@@ -595,21 +709,17 @@ module Squared
595
709
  end
596
710
  case key
597
711
  when :dependfile
598
- if val.first.nil?
599
- super
712
+ req = basepath(*val)
713
+ if (index = DEP_PYTHON.index(req.basename.to_s))
714
+ @dependindex = index
715
+ @dependfile = req
600
716
  else
601
- req = basepath(*val)
602
- if (index = DEP_PYTHON.index(req.basename.to_s))
603
- @dependindex = index
604
- @dependfile = req
605
- else
606
- log.warn "variable_set: @#{key}=#{req} (not supported)"
607
- end
717
+ log.warn "variable_set: @#{key}=#{req} (not supported)"
608
718
  end
609
719
  when :editable
610
720
  editable_set val.first
611
721
  when :venv
612
- @venv = val.empty? || val.first.nil? ? nil : basepath(*val)
722
+ instance_variable_set(:"@#{key}", (basepath(*val) unless val.empty?))
613
723
  else
614
724
  super
615
725
  end
@@ -620,7 +730,7 @@ module Squared
620
730
  end
621
731
 
622
732
  def outdated?
623
- dependtype > 0 && !task_pass?('outdated')
733
+ dependtype > 0
624
734
  end
625
735
 
626
736
  private
@@ -630,18 +740,16 @@ module Squared
630
740
  end
631
741
 
632
742
  def python_session(*cmd, opts: nil)
633
- pre = preopts(quiet: false)
634
- return session('python', *pre, *cmd, path: venv.nil?) unless opts
743
+ return session('python', *preopts(quiet: false), *cmd, path: venv.nil?) unless opts
635
744
 
636
- op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
637
- ret = session('python', *pre, *op.to_a, *cmd, path: venv.nil?)
638
- [ret, op.extras]
745
+ op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: /\A(?:v+|OO)\z/)
746
+ [session('python', *op.to_a, *cmd, path: venv.nil?), op.extras]
639
747
  end
640
748
 
641
749
  def poetry_session(*cmd)
642
- ret = session('poetry', *cmd, *preopts)
643
- option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
644
- ret
750
+ session('poetry', *cmd, *preopts).tap do |ret|
751
+ option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
752
+ end
645
753
  end
646
754
 
647
755
  def pdm_session(*cmd, opts: nil)
@@ -655,19 +763,29 @@ module Squared
655
763
  def create_session(*cmd, name:, common:, opts: nil)
656
764
  return session(name, *preopts, *cmd, path: venv.nil?) unless opts
657
765
 
658
- op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
659
- ret = session(name, *preopts, *op.to_a, *cmd, path: venv.nil?)
660
- [ret, op.extras]
766
+ op = OptionPartition.new(opts, common, project: self, single: singleopt)
767
+ [session(name, *op.to_a, *cmd, path: venv.nil?), op.extras]
661
768
  end
662
769
 
663
770
  def append_pip(flag, opts, target: @session, from: nil)
664
- unless from && !opts.empty?
665
- append_global(target: target)
666
- return []
771
+ list = OPT_PIP.fetch(from, []) + OPT_PIP[:common]
772
+ if pip_install?(flag)
773
+ list.concat(OPT_PIP[:install_a])
774
+ list.concat(OPT_PIP[:install_b]) unless flag == :index
775
+ case flag
776
+ when :install
777
+ list.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
778
+ when :lock, :wheel
779
+ list.concat(OPT_PIP[:install_c])
780
+ when :download, :index
781
+ list.concat(OPT_PIP[:debug])
782
+ end
783
+ opts << 'no-build-isolation' if option('build-isolation', equals: '0')
667
784
  end
668
- op = OptionPartition.new(opts, OPT_PIP[from] + OPT_PIP[:common], target, project: self, single: singleopt)
785
+ op = OptionPartition.new(opts, list, target, project: self, single: singleopt)
669
786
  append_global(target: target)
670
- if from == :install
787
+ case flag
788
+ when :install, :lock, :wheel
671
789
  edit = nil
672
790
  op.each do |opt|
673
791
  if opt =~ op.values
@@ -682,7 +800,6 @@ module Squared
682
800
  op.found << opt
683
801
  end
684
802
  end
685
- op << '--no-build-isolation' if option('build-isolation', equals: '0')
686
803
  op.swap
687
804
  if edit
688
805
  edit = basepath(edit) unless %r{\A[a-z]+(?:\+[a-z]+)?://}i.match?(edit)
@@ -692,16 +809,8 @@ module Squared
692
809
  op << quote_option('e', edit)
693
810
  end
694
811
  end
695
- case flag
696
- when :editable, :upgrade
697
- op.extras
698
- else
699
- op.clear
700
- []
701
- end
702
- else
703
- op.extras
704
812
  end
813
+ op
705
814
  end
706
815
 
707
816
  def append_editable(target: @session)
@@ -711,29 +820,29 @@ module Squared
711
820
  OptionPartition.delete_key(target, 'e', 'editable')
712
821
  case val
713
822
  when '0', 'false'
714
- return unless installable?
823
+ return
715
824
  else
716
825
  val = basepath val
717
826
  end
718
- elsif session_arg?('e', 'editable', target: target) || !installable?
827
+ elsif session_arg?('e', 'editable', target: target) || !(val = editable)
719
828
  return
720
- else
721
- val = editable
722
829
  end
723
- target << (val ? quote_option('e', basepath(val)) : '.')
830
+ target << quote_option('e', basepath(val))
724
831
  end
725
832
 
726
833
  def append_global(target: @session)
727
- option('cache-dir', target: target) do |val|
728
- target << case val
729
- when '0', 'false'
730
- '--no-cache-dir'
731
- else
732
- quote_option('cache-dir', basepath(val))
733
- end
734
- end
735
- option('proxy', target: target) { |val| target << quote_option('proxy', val) }
736
- option('python', target: target) { |val| target << quote_option('python', basepath(val)) }
834
+ target.merge([
835
+ option('cache-dir', target: target) do |val|
836
+ case val
837
+ when '0', 'false'
838
+ '--no-cache-dir'
839
+ else
840
+ quote_option 'cache-dir', basepath(val)
841
+ end
842
+ end,
843
+ option('proxy', target: target) { |val| shell_option('proxy', val) },
844
+ option('python', target: target) { |val| quote_option('python', basepath(val)) }
845
+ ])
737
846
  append_nocolor(target: target)
738
847
  end
739
848
 
@@ -782,13 +891,13 @@ module Squared
782
891
  end
783
892
  end
784
893
  else
785
- val = case (val = data[2])
894
+ val = case (ch = data[2])
786
895
  when 'true'
787
896
  true
788
897
  when 'false'
789
898
  false
790
899
  else
791
- val.include?('.') ? val.to_f : val.to_i
900
+ ch.include?('.') ? ch.to_f : ch.to_i
792
901
  end
793
902
  end
794
903
  ret << [data[1], val]
@@ -805,15 +914,15 @@ module Squared
805
914
  end
806
915
 
807
916
  def pyprojectfile
808
- return unless (ret = basepath(DEP_PYTHON[2])).exist?
917
+ return @pyprojectfile || nil unless @pyprojectfile.nil?
809
918
 
810
- ret
919
+ @pyprojectfile = (file = basepath(DEP_PYTHON[2])).exist? ? file : false
811
920
  end
812
921
 
813
922
  def singleopt(flag = nil)
814
923
  case flag
815
924
  when :python
816
- /\A(?:v+|q+|b+|V+|O+)\z/
925
+ /\A(?:v+|q+|b+)\z/
817
926
  when :twine
818
927
  nil
819
928
  else
@@ -822,14 +931,14 @@ module Squared
822
931
  end
823
932
 
824
933
  def preopts(quiet: true)
825
- ret = []
826
- case verbose
827
- when FalseClass
828
- ret << '--quiet' if quiet
829
- when Numeric
830
- ret << "-#{'v' * verbose}" if verbose > 0
934
+ [].tap do |ret|
935
+ case verbose
936
+ when FalseClass
937
+ ret << '--quiet' if quiet
938
+ when Numeric
939
+ ret << "-#{'v' * verbose}" if verbose > 0
940
+ end
831
941
  end
832
- ret
833
942
  end
834
943
 
835
944
  def variables
@@ -896,10 +1005,30 @@ module Squared
896
1005
  .clear(pass: false)
897
1006
  .arg?(/\A-v+\z/)
898
1007
  run(op, env, exception: true, banner: banner)
899
- install(:upgrade, ['poetry']) if poetry?
900
1008
  puts(dir.directory? ? "Success: #{dir}" : 'Failed') if banner && !status
901
1009
  end
902
1010
 
1011
+ def pip_install?(flag)
1012
+ %i[install download index lock wheel].include?(flag)
1013
+ end
1014
+
1015
+ def backend?(flag)
1016
+ case flag
1017
+ when :poetry
1018
+ build_backend == 'poetry.core.masonry.api'
1019
+ when :pdm
1020
+ build_backend == 'pdm.backend'
1021
+ when :hatch
1022
+ build_backend == 'hatchling.build'
1023
+ when :setuptools
1024
+ build_backend == 'setuptools.build_meta'
1025
+ end
1026
+ end
1027
+
1028
+ def installable?
1029
+ setuptools? || !pyprojectfile.nil?
1030
+ end
1031
+
903
1032
  def setuptools?
904
1033
  dependtype == 2 || dependtype == 4
905
1034
  end