squared 0.5.19 → 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,24 +8,39 @@ 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,
@@ -52,11 +67,18 @@ module Squared
52
67
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
53
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
@@ -361,11 +392,12 @@ module Squared
361
392
  when :force
362
393
  cmd << '--force-reinstall'
363
394
  end
364
- append_pip(flag, opts, from: :install)
395
+ op = append_pip(flag, opts, from: :install)
396
+ op.clear
365
397
  else
366
398
  append_global
367
399
  end
368
- 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')
369
401
  append_editable
370
402
  end
371
403
  run(from: :depend, sync: sync)
@@ -374,18 +406,24 @@ module Squared
374
406
 
375
407
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
376
408
  cmd = pip_session 'list --outdated'
409
+ cmd << '--not-required' unless flag || option('not-required', equals: '0')
410
+ cmd << '--local' if option('l', 'local')
377
411
  append_global
378
412
  cmd = session_done cmd
379
413
  log.info cmd
380
414
  on :first, :outdated
381
415
  banner = format_banner cmd
382
- print_item banner if sync
383
- start = 0
384
- found = 0
385
- major = []
386
- minor = []
387
- patch = []
416
+ items = if sync
417
+ print_item banner
418
+ [] if flag && has_value?(opts, 's', 'select') && !stdin?
419
+ end
388
420
  pwd_set(from: :outdated) do
421
+ tc = theme[:current]
422
+ start = 0
423
+ found = 0
424
+ major = []
425
+ minor = []
426
+ patch = []
389
427
  buffer = []
390
428
  out = ->(val) { sync ? puts(val) : buffer << val }
391
429
  if workspace.windows?
@@ -419,26 +457,27 @@ module Squared
419
457
  styles = color(:yellow)
420
458
  else
421
459
  styles = color(:green)
422
- line = sub_style(line, pat: /^(\S+)(.+)$/, styles: if type == 2
423
- styles << :bold
424
- theme[:major]
425
- else
426
- theme[:active]
427
- 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+)(.+)$/)
428
466
  end
429
- if theme[:current]
430
- line = sub_style(line, pat: /^(.+)(#{Regexp.escape(current)})(.+)$/, styles: theme[:current],
431
- index: 2)
432
- end
433
- 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))
434
469
  found += 1
435
470
  end
436
- 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
437
476
  start += 1
438
477
  elsif line.start_with?('Package')
439
478
  unless stdin?
440
- sub = { pat: /^(.*)(?<!\dm)(Package|Latest)(.+)$/, styles: theme[:header], index: 2 }
441
- 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))
442
481
  end
443
482
  start += 1
444
483
  end
@@ -448,17 +487,23 @@ module Squared
448
487
  puts buffer
449
488
  end
450
489
  if found > 0
451
- print_status(major.size, minor.size, patch.size, from: :outdated)
452
- pkg = case flag
453
- when :major
454
- major + minor + patch
455
- when :minor
456
- minor + patch
457
- when :patch
458
- patch
459
- end
460
- unless !pkg || pkg.empty?
461
- 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)
462
507
  end
463
508
  elsif start == 0
464
509
  puts 'No updates were found'
@@ -467,84 +512,95 @@ module Squared
467
512
  on :last, :outdated
468
513
  end
469
514
 
470
- def install(flag, opts = [], strategy: nil, user: nil)
471
- cmd = pip_session 'install'
472
- 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)
473
517
  case flag
474
518
  when :editable
475
- cmd << quote_option('e', out.pop || editable || '.')
476
- option_clear out
519
+ op << quote_option('e', op.pop || editable || '.')
520
+ op.clear
477
521
  when :upgrade
478
- raise_error('no packages listed', hint: flag) if out.empty?
479
- cmd << '--upgrade'
480
- cmd << '--user' if user
481
- cmd << basic_option('upgrade-strategy', strategy) if strategy
482
- append_value out
483
- 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?
484
527
  end
485
- run(from: :install)
528
+ run(banner: banner, from: :install)
486
529
  end
487
530
 
488
531
  def build!(flag, opts = [])
489
- case flag
490
- when :poetry
491
- cmd = poetry_session 'build'
492
- list = OPT_POETRY[:build] + OPT_POETRY[:common]
493
- when :pdm
494
- cmd, opts = pdm_session('build', opts: opts)
495
- list = OPT_PDM[:build]
496
- when :hatch
497
- cmd, opts = hatch_session('build', opts: opts)
498
- list = OPT_HATCH[:build]
499
- else
500
- cmd, opts = python_session('-m build', opts: opts)
501
- list = OPT_PYTHON[:build]
502
- 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
503
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
504
556
  case flag
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
566
+ end
505
567
  when :hatch
506
- if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
507
- op.add_path(outdir)
568
+ if ENV['HATCH_BUILD_LOCATION']
569
+ srcdir = nil
570
+ else
571
+ srcdir ||= path
508
572
  end
509
573
  op << basic_option('p', project) unless ENV['HATCH_PROJECT'] || op.arg?('p', 'project')
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)
522
- end
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)
631
+ case flag
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)
575
636
  case flag
576
- when :uninstall
577
- raise_error('no packages listed', hint: flag) if out.empty?
578
- cmd.merge(out)
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)
@@ -605,7 +719,7 @@ module Squared
605
719
  when :editable
606
720
  editable_set val.first
607
721
  when :venv
608
- instance_variable_set(:"@#{key}", val.empty? ? nil : basepath(*val))
722
+ instance_variable_set(:"@#{key}", (basepath(*val) unless val.empty?))
609
723
  else
610
724
  super
611
725
  end
@@ -616,7 +730,7 @@ module Squared
616
730
  end
617
731
 
618
732
  def outdated?
619
- dependtype > 0 && !task_pass?('outdated')
733
+ dependtype > 0
620
734
  end
621
735
 
622
736
  private
@@ -628,15 +742,14 @@ module Squared
628
742
  def python_session(*cmd, opts: nil)
629
743
  return session('python', *preopts(quiet: false), *cmd, path: venv.nil?) unless opts
630
744
 
631
- op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
632
- ret = session('python', *op.to_a, *cmd, path: venv.nil?)
633
- [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]
634
747
  end
635
748
 
636
749
  def poetry_session(*cmd)
637
- ret = session('poetry', *cmd, *preopts)
638
- option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
639
- ret
750
+ session('poetry', *cmd, *preopts).tap do |ret|
751
+ option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
752
+ end
640
753
  end
641
754
 
642
755
  def pdm_session(*cmd, opts: nil)
@@ -651,18 +764,28 @@ module Squared
651
764
  return session(name, *preopts, *cmd, path: venv.nil?) unless opts
652
765
 
653
766
  op = OptionPartition.new(opts, common, project: self, single: singleopt)
654
- ret = session(name, *op.to_a, *cmd, path: venv.nil?)
655
- [ret, op.extras]
767
+ [session(name, *op.to_a, *cmd, path: venv.nil?), op.extras]
656
768
  end
657
769
 
658
770
  def append_pip(flag, opts, target: @session, from: nil)
659
- unless from && !opts.empty?
660
- append_global(target: target)
661
- 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')
662
784
  end
663
- 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)
664
786
  append_global(target: target)
665
- if from == :install
787
+ case flag
788
+ when :install, :lock, :wheel
666
789
  edit = nil
667
790
  op.each do |opt|
668
791
  if opt =~ op.values
@@ -677,7 +800,6 @@ module Squared
677
800
  op.found << opt
678
801
  end
679
802
  end
680
- op << '--no-build-isolation' if option('build-isolation', equals: '0')
681
803
  op.swap
682
804
  if edit
683
805
  edit = basepath(edit) unless %r{\A[a-z]+(?:\+[a-z]+)?://}i.match?(edit)
@@ -687,16 +809,8 @@ module Squared
687
809
  op << quote_option('e', edit)
688
810
  end
689
811
  end
690
- case flag
691
- when :editable, :upgrade
692
- op.extras
693
- else
694
- op.clear
695
- []
696
- end
697
- else
698
- op.extras
699
812
  end
813
+ op
700
814
  end
701
815
 
702
816
  def append_editable(target: @session)
@@ -717,16 +831,18 @@ module Squared
717
831
  end
718
832
 
719
833
  def append_global(target: @session)
720
- option('cache-dir', target: target) do |val|
721
- target << case val
722
- when '0', 'false'
723
- '--no-cache-dir'
724
- else
725
- quote_option('cache-dir', basepath(val))
726
- end
727
- end
728
- option('proxy', target: target) { |val| target << quote_option('proxy', val) }
729
- 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
+ ])
730
846
  append_nocolor(target: target)
731
847
  end
732
848
 
@@ -775,13 +891,13 @@ module Squared
775
891
  end
776
892
  end
777
893
  else
778
- val = case (val = data[2])
894
+ val = case (ch = data[2])
779
895
  when 'true'
780
896
  true
781
897
  when 'false'
782
898
  false
783
899
  else
784
- val.include?('.') ? val.to_f : val.to_i
900
+ ch.include?('.') ? ch.to_f : ch.to_i
785
901
  end
786
902
  end
787
903
  ret << [data[1], val]
@@ -798,15 +914,15 @@ module Squared
798
914
  end
799
915
 
800
916
  def pyprojectfile
801
- return unless (ret = basepath(DEP_PYTHON[2])).exist?
917
+ return @pyprojectfile || nil unless @pyprojectfile.nil?
802
918
 
803
- ret
919
+ @pyprojectfile = (file = basepath(DEP_PYTHON[2])).exist? ? file : false
804
920
  end
805
921
 
806
922
  def singleopt(flag = nil)
807
923
  case flag
808
924
  when :python
809
- /\A(?:v+|q+|b+|V+|O+)\z/
925
+ /\A(?:v+|q+|b+)\z/
810
926
  when :twine
811
927
  nil
812
928
  else
@@ -815,14 +931,14 @@ module Squared
815
931
  end
816
932
 
817
933
  def preopts(quiet: true)
818
- ret = []
819
- case verbose
820
- when FalseClass
821
- ret << '--quiet' if quiet
822
- when Numeric
823
- 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
824
941
  end
825
- ret
826
942
  end
827
943
 
828
944
  def variables
@@ -892,6 +1008,27 @@ module Squared
892
1008
  puts(dir.directory? ? "Success: #{dir}" : 'Failed') if banner && !status
893
1009
  end
894
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
+
895
1032
  def setuptools?
896
1033
  dependtype == 2 || dependtype == 4
897
1034
  end