squared 0.4.14 → 0.5.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,7 +8,7 @@ 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 OO P q s S u v x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
11
- build: %w[n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel C|config-setting=q installer=b
11
+ build: %w[n|no-isolation s|sdist v|verbose w|wheel x|skip-dependency-check 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
@@ -28,18 +28,11 @@ module Squared
28
28
  freeze: %w[all exclude-editable l|local user exclude=b path=p r|requirement=p].freeze
29
29
  }.freeze
30
30
  OPT_POETRY = {
31
- common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose P|project=p].freeze,
31
+ common: %w[ansi no-ansi no-cache n|no-interaction no-plugins P|project=p q|quiet v|verbose].freeze,
32
32
  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=b r|repository=b
33
+ publish: %w[build dry-run client-cert=p cert=p dist-dir=p p|password=b r|repository=b skip-existing
34
34
  u|username=b].freeze
35
35
  }.freeze
36
- OPT_PDM = {
37
- common: %w[I|ignore-python no-cache n|non-interactive].freeze,
38
- build: %w[C=bm no-clean no-isolation no-sdist no-wheel quiet verbose config-setting=q d|dest=p p|project=p
39
- k|skip=b].freeze,
40
- publish: %w[no-build no-very-ssl quiet S|sign skip-existing verbose ca-certs=p c|comment=q d|dest=p identity=b
41
- p|password=q p|project=p r|repository=q k|skip=b u|username=b].freeze
42
- }.freeze
43
36
  OPT_HATCH = {
44
37
  common: %w[color interactive no-color no-interactive cache-dir=p config=p data-dir=p e|env=b p|project=b
45
38
  q|quiet v|verbose].freeze,
@@ -48,11 +41,11 @@ module Squared
48
41
  p|publisher=b r|repo=b u|user=q].freeze
49
42
  }.freeze
50
43
  OPT_TWINE = {
51
- publish: %w[attestations disable-progress-bar non-interactive s|sign skip-existing verbose cert=p
52
- client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
44
+ publish: %w[attestations disable-progress-bar non-interactive skip-existing verbose s|sign c|comment=q
45
+ config-file=p cert=p client-cert=p i|identity=b p|password=q r|repository=b repository-url=q
53
46
  sign-with=b u|username=q].freeze
54
47
  }.freeze
55
- private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE
48
+ private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_HATCH, :OPT_TWINE
56
49
 
57
50
  class << self
58
51
  def populate(*); end
@@ -78,7 +71,7 @@ module Squared
78
71
 
79
72
  attr_reader :venv, :editable
80
73
 
81
- def initialize(*, editable: '.', verbose: nil, **kwargs)
74
+ def initialize(*, venv: nil, editable: '.', verbose: nil, **kwargs)
82
75
  super
83
76
  if @pass.include?(Python.ref)
84
77
  initialize_ref Python.ref
@@ -87,20 +80,18 @@ module Squared
87
80
  initialize_build(Python.ref, **kwargs)
88
81
  initialize_env(**kwargs)
89
82
  end
90
- @verbose = verbose.size if verbose.is_a?(String) && verbose.match?(/\Av+\z/)
91
83
  dependfile_set DEP_PYTHON
84
+ @verbose = verbose.size if verbose.is_a?(String) && verbose.match?(/\Av+\z/)
92
85
  editable_set editable
93
- venv_set kwargs[:venv]
86
+ venv_set venv if venv
94
87
  end
95
88
 
96
89
  subtasks({
97
90
  'venv' => %i[exec create remove show].freeze,
98
91
  'pip' => %i[uninstall freeze].freeze,
99
92
  'install' => %i[user force upgrade target editable].freeze,
100
- 'outdated' => %i[major minor patch].freeze,
101
- 'build' => %i[python poetry pdm hatch].freeze,
102
- 'publish' => %i[poetry pdm hatch twine].freeze,
103
- 'run' => nil,
93
+ 'build' => %i[python poetry hatch].freeze,
94
+ 'publish' => %i[poetry twine hatch].freeze,
104
95
  'exec' => nil
105
96
  })
106
97
 
@@ -118,62 +109,6 @@ module Squared
118
109
 
119
110
  if flags.nil?
120
111
  case action
121
- when 'run'
122
- next unless pyprojectfile
123
-
124
- format_desc action, nil, "script+|#{indexchar}index+|#,pattern*"
125
- task action, [:command] do |_, args|
126
- found = 0
127
- ['tool.poetry.scripts', 'tool.pdm.scripts', 'project.scripts'].each_with_index do |table, index|
128
- next if (list = read_pyproject(table)).empty?
129
-
130
- if args.command == '#'
131
- format_list(list, "run[#{indexchar}N]", 'scripts', grep: args.extras, from: pyprojectfile)
132
- found |= 1
133
- else
134
- args.to_a.each do |val|
135
- if (n, = indexitem(val))
136
- if (script, = list[n - 1])
137
- case index
138
- when 0
139
- script = session_output 'poetry', 'run', script
140
- when 1
141
- script = pdm_session 'run', script
142
- else
143
- venv_init
144
- end
145
- found |= 1
146
- run(script, from: :run)
147
- elsif exception
148
- indexerror n, list
149
- else
150
- found |= 2
151
- log.warn "run script #{n} of #{list.size} (out of range)"
152
- end
153
- else
154
- case index
155
- when 0
156
- found |= 1
157
- run(session_output('poetry', 'run', val), from: :run)
158
- when 1
159
- found |= 1
160
- run(pdm_session('run', val), from: :run)
161
- else
162
- raise_error("script: #{val}", hint: 'unknown') if exception
163
- found |= 2
164
- log.warn "run script \"#{val}\" (not indexed)"
165
- end
166
- end
167
- end
168
- end
169
- break
170
- end
171
- unless found.anybits?(1)
172
- puts log_message(found == 0 ? Logger::INFO : Logger.WARN,
173
- "no scripts #{found == 0 ? 'found' : 'executed'}",
174
- subject: name, hint: pyprojectfile)
175
- end
176
- end
177
112
  when 'exec'
178
113
  format_desc action, nil, 'command|:,args*'
179
114
  task action do |_, args|
@@ -188,7 +123,7 @@ module Squared
188
123
  end
189
124
  args.join(' ')
190
125
  end
191
- shell(cmd, name: :exec, chdir: path)
126
+ Kernel.exec(cmd, chdir: path)
192
127
  end
193
128
  end
194
129
  else
@@ -279,30 +214,14 @@ module Squared
279
214
  depend flag, args.to_a
280
215
  end
281
216
  end
282
- when 'outdated'
283
- format_desc action, flag, 'eager?'
284
- task flag do |_, args|
285
- outdated flag, args.to_a
286
- end
287
217
  when 'build'
288
- case flag
289
- when :poetry
290
- next unless build_backend == 'poetry.core.masonry.api'
291
- when :pdm
292
- next unless build_backend == 'pdm.backend'
293
- when :hatch
294
- next unless build_backend == 'hatchling.build'
295
- end
296
218
  format_desc(action, flag, 'opts*', after: case flag
297
219
  when :python then 'srcdir?'
298
- when :poetry then 'output?'
299
- when :pdm then 'dest?'
300
220
  when :hatch then 'location?'
301
221
  end)
302
222
  task flag do |_, args|
303
223
  build! flag, args.to_a
304
224
  end
305
- break unless flag == :python
306
225
  when 'publish'
307
226
  format_desc(action, flag, 'opts*', after: case flag
308
227
  when :hatch then 'artifacts?'
@@ -350,7 +269,7 @@ module Squared
350
269
  end
351
270
  end
352
271
 
353
- def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated'))
272
+ def outdated(*, sync: invoked_sync?('outdated'))
354
273
  cmd = pip_session 'list', '--outdated'
355
274
  append_global
356
275
  cmd = session_done cmd
@@ -360,35 +279,28 @@ module Squared
360
279
  print_item banner if sync
361
280
  start = 0
362
281
  found = 0
363
- major = []
364
- minor = []
365
- patch = []
282
+ major = 0
366
283
  pwd_set(from: :outdated) do
367
284
  buffer = []
368
285
  out = ->(val) { sync ? puts(val) : buffer << val }
369
286
  IO.popen(runenv || {}, cmd).each do |line|
370
- next if line.match?(/^[ -]+$/)
287
+ next if line.match?(/^[\s-]+$/)
371
288
 
372
289
  if start > 0
373
290
  unless stdin?
374
- cur, lat = line.scan(SEM_VER)
375
- next unless cur && lat
291
+ data = line.scan(SEM_VER)
292
+ next unless (cur = data.shift) && (lat = data.shift)
376
293
 
377
294
  latest = lat.join
378
295
  current = cur.join
379
296
  semver cur
380
297
  semver lat
381
- name = line.split(' ', 2).first
382
- type = if semmajor?(cur, lat)
383
- major << name
384
- 2
385
- elsif cur[2] == lat[2]
386
- patch << name
387
- 0
388
- else
389
- minor << name
390
- 1
391
- end
298
+ if semmajor?(cur, lat)
299
+ type = 2
300
+ major += 1
301
+ else
302
+ type = cur[2] == lat[2] ? 0 : 1
303
+ end
392
304
  if type == 0
393
305
  styles = color(:yellow)
394
306
  else
@@ -422,16 +334,7 @@ module Squared
422
334
  puts buffer
423
335
  end
424
336
  if found > 0
425
- print_status(major.size, minor.size, patch.size, from: :outdated)
426
- pkg = case flag
427
- when :major
428
- major + minor + patch
429
- when :minor
430
- minor + patch
431
- when :patch
432
- patch
433
- end
434
- install(:upgrade, pkg, strategy: opts.include?('eager') ? 'eager' : nil) unless !pkg || pkg.empty?
337
+ puts print_footer empty_status('Updates are available', 'major', major)
435
338
  elsif start == 0
436
339
  puts 'No updates were found'
437
340
  end
@@ -463,9 +366,6 @@ module Squared
463
366
  when :poetry
464
367
  cmd = poetry_session 'build'
465
368
  list = OPT_POETRY[:build] + OPT_POETRY[:common]
466
- when :pdm
467
- cmd, opts = pdm_session('build', opts: opts)
468
- list = OPT_PDM[:build]
469
369
  when :hatch
470
370
  cmd, opts = hatch_session('build', opts: opts)
471
371
  list = OPT_HATCH[:build]
@@ -473,7 +373,7 @@ module Squared
473
373
  srcdir = nil
474
374
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
475
375
  op.each do |opt|
476
- if !srcdir && basepath(opt.chomp('*')).exist? && projectpath?(opt.chomp('*'))
376
+ if !srcdir && basepath(opt).exist? && projectpath?(opt)
477
377
  srcdir = opt
478
378
  else
479
379
  op.found << opt
@@ -481,13 +381,12 @@ module Squared
481
381
  end
482
382
  op.swap
483
383
  case flag
484
- when :poetry, :pdm
384
+ when :poetry
485
385
  if srcdir
486
- args = flag == :pdm ? ['d', 'dest'] : ['o', 'output']
487
- if op.arg?(*args)
386
+ if op.arg?('o', 'output')
488
387
  op.extras << srcdir
489
388
  else
490
- op << quote_option(args.last, path + srcdir)
389
+ op << quote_option('output', path + srcdir)
491
390
  end
492
391
  srcdir = nil
493
392
  end
@@ -509,35 +408,21 @@ module Squared
509
408
  when :poetry
510
409
  poetry_session 'publish'
511
410
  list = OPT_POETRY[:publish] + OPT_POETRY[:common]
512
- when :pdm
513
- opts = pdm_session('publish', opts: opts).last
514
- list = OPT_PDM[:publish]
515
- when :hatch
516
- opts = hatch_session('publish', opts: opts).last
517
- list = OPT_HATCH[:publish]
518
411
  when :twine
519
412
  session 'twine', 'upload'
520
413
  list = OPT_TWINE[:publish]
414
+ when :hatch
415
+ opts = hatch_session('publish', opts: opts).last
416
+ list = OPT_HATCH[:publish]
521
417
  end
522
418
  op = OptionPartition.new(opts, list, @session, project: self, single: singleopt(flag))
523
- dist = lambda do
524
- (path + 'dist').tap do |dir|
525
- raise_error('no source files found', hint: dir) unless dir.directory? && !dir.empty?
526
- end
419
+ if op.empty?
420
+ dist = path + 'dist'
421
+ raise_error('no source files found', hint: dist) unless dist.directory? && !dist.empty?
422
+ op.extras << "#{dist}/*" unless flag == :poetry
527
423
  end
528
- case flag
529
- when :hatch, :twine
530
- if op.empty?
531
- op.extras << "#{dist.call}/*"
532
- else
533
- op.map! { |val| path + val }
534
- end
535
- op.append
536
- else
537
- dist.call unless op.arg?(*(flag == :poetry ? ['dist-dir'] : ['d', 'dest']))
538
- op.clear(pass: false)
539
- end
540
- run(from: :"#{flag}:publish", interactive: "Publish #{sub_style(project, styles: theme[:active])}")
424
+ op.append
425
+ run(from: :"#{flag}:publish")
541
426
  end
542
427
 
543
428
  def pip(flag, opts = [])
@@ -557,7 +442,13 @@ module Squared
557
442
  ret
558
443
  end
559
444
 
560
- def variable_set(key, *val, **)
445
+ def variable_set(key, *val, **, &blk)
446
+ if block_given?
447
+ case key
448
+ when :dependfile, :venv, :editable
449
+ val = block_args val, &blk
450
+ end
451
+ end
561
452
  case key
562
453
  when :dependfile
563
454
  req = basepath(*val)
@@ -600,25 +491,15 @@ module Squared
600
491
 
601
492
  def poetry_session(*cmd)
602
493
  ret = session('poetry', *cmd, *preopts)
603
- if (val = option('project', ignore: false))
604
- ret << quote_option('project', path + val)
605
- end
494
+ option('project', ignore: false) { |val| ret << quote_option('project', path + val) }
606
495
  ret
607
496
  end
608
497
 
609
- def pdm_session(*cmd, opts: nil)
610
- create_session(*cmd, name: 'pdm', common: OPT_PDM[:common], opts: opts)
611
- end
612
-
613
498
  def hatch_session(*cmd, opts: nil)
614
- create_session(*cmd, name: 'hatch', common: OPT_HATCH[:common], opts: opts)
615
- end
499
+ return session('hatch', *preopts, *cmd, path: venv.nil?) unless opts
616
500
 
617
- def create_session(*cmd, name:, common:, opts: nil)
618
- return session(name, *preopts, *cmd, path: venv.nil?) unless opts
619
-
620
- op = OptionPartition.new(opts, common, project: self, single: singleopt)
621
- ret = session(name, *op.to_a, *cmd, path: venv.nil?)
501
+ op = OptionPartition.new(opts, OPT_HATCH[:common], project: self, single: singleopt)
502
+ ret = session('hatch', *op.to_a, *cmd, path: venv.nil?)
622
503
  [ret, op.extras]
623
504
  end
624
505
 
@@ -683,7 +564,7 @@ module Squared
683
564
  end
684
565
 
685
566
  def append_global(target: @session)
686
- if (val = option('cache-dir', target: target))
567
+ option('cache-dir', target: target) do |val|
687
568
  target << case val
688
569
  when '0', 'false'
689
570
  '--no-cache-dir'
@@ -691,82 +572,18 @@ module Squared
691
572
  quote_option('cache-dir', path + val)
692
573
  end
693
574
  end
694
- target << shell_option('proxy', val) if (val = option('proxy', target: target))
695
- target << quote_option('python', path + val) if (val = option('python', target: target))
575
+ option('proxy', target: target) { |val| target << shell_option('proxy', val) }
576
+ option('python', target: target) { |val| target << quote_option('python', path + val) }
696
577
  append_nocolor(target: target)
697
578
  end
698
579
 
699
- def build_backend
700
- @build_backend ||= read_pyproject('build-system', 'build-backend') || ''
701
- end
702
-
703
- def read_pyproject(table, key = nil)
704
- return [] unless (file = pyprojectfile)
705
-
706
- unless (ret = (@pyproject ||= {})[table])
707
- ret = []
708
- start = /^\s*\[#{Regexp.escape(table)}\]\s*$/
709
- ch = nil
710
- found = false
711
- File.foreach(file) do |line|
712
- if found
713
- break if line.match?(/^\s*\[[\w.-]+\]\s*$/)
714
-
715
- if ch
716
- val = line.rstrip
717
- case ch
718
- when '}', ']'
719
- ch = nil if val.end_with?(ch)
720
- val = "\n#{val}"
721
- else
722
- if val.chomp!(ch)
723
- ch = nil
724
- else
725
- val = line
726
- end
727
- end
728
- ret.last[1] += val
729
- elsif (data = line.match(/^\s*(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*?))\s*$/))
730
- if (val = data[4])
731
- case (ch = data[3])
732
- when '{', '['
733
- val = "#{ch}#{val}"
734
- ch = ch == '{' ? '}' : ']'
735
- ch = nil if val.end_with?(ch)
736
- else
737
- if val.chomp!(ch)
738
- ch = nil
739
- elsif ch.size == 1
740
- next
580
+ def editable_set(val)
581
+ @editable = case val
582
+ when '.', Pathname
583
+ val
584
+ when String
585
+ Pathname.new(editable)
741
586
  end
742
- end
743
- else
744
- val = case (val = data[2])
745
- when 'true'
746
- true
747
- when 'false'
748
- false
749
- else
750
- val.include?('.') ? val.to_f : val.to_i
751
- end
752
- end
753
- ret << [data[1], val]
754
- end
755
- else
756
- found = line.match?(start)
757
- end
758
- end
759
- @pyproject[table] = ret
760
- end
761
- return ret.find { |val| val[0] == key }&.last if key
762
-
763
- ret
764
- end
765
-
766
- def pyprojectfile
767
- return unless (ret = basepath(DEP_PYTHON[2])).exist?
768
-
769
- ret
770
587
  end
771
588
 
772
589
  def singleopt(flag = nil)
@@ -809,18 +626,7 @@ module Squared
809
626
  @venv&.join(workspace.windows? ? 'Scripts' : 'bin')
810
627
  end
811
628
 
812
- def editable_set(val)
813
- @editable = case val
814
- when '.', Pathname
815
- val
816
- when String
817
- Pathname.new(editable)
818
- end
819
- end
820
-
821
629
  def venv_set(val)
822
- return unless val
823
-
824
630
  if val.is_a?(Array)
825
631
  val, *opts = val
826
632
  @venvopts = opts