squared 0.4.5 → 0.4.7

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.
@@ -5,7 +5,7 @@ module Squared
5
5
  module Project
6
6
  class Node < Git
7
7
  OPT_NPM = {
8
- common: %w[dry-run include-workspace-root workspaces=b? w|workspace=b].freeze,
8
+ common: %w[dry-run include-workspace-root workspaces=b? w|workspace=v].freeze,
9
9
  install: %w[prefer-dedupe package-lock-only cpu=b libc=b os=b].freeze,
10
10
  install_base: %w[ignore-scripts install-links strict-peer-deps include=b omit=b install-strategy=b].freeze,
11
11
  install_no: %w[audit bin-links fund package-lock].freeze,
@@ -238,8 +238,11 @@ module Squared
238
238
  return
239
239
  end
240
240
  items = []
241
- items << @workspace.home if build? && path != @workspace.home && @workspace.home?
242
- items += as_a(also) if also
241
+ if build? && path != @workspace.home && @workspace.home?
242
+ items << @workspace.home
243
+ @workspace.rev_clear(@workspace.find(@workspace.home).name)
244
+ end
245
+ items.concat(as_a(also)) if also
243
246
  return if items.empty?
244
247
 
245
248
  on :first, :copy
@@ -248,11 +251,18 @@ module Squared
248
251
  case dir
249
252
  when Pathname
250
253
  dest = dir
254
+ @workspace.rev_clear(dest)
251
255
  when String
252
256
  dest = @workspace.rootpath(dir)
257
+ @workspace.rev_clear(dest)
253
258
  when Symbol
254
- dest = @workspace.find(name: dir)&.path
255
- log.warn message("copy project :#{dir}", hint: 'not found') unless dest
259
+ if (proj = @workspace.find(name: dir))
260
+ @workspace.rev_clear(proj.name)
261
+ dest = proj.path
262
+ else
263
+ log.warn message("copy project :#{dir}", hint: 'not found')
264
+ dest = nil
265
+ end
256
266
  when Hash
257
267
  from = dir[:from] if dir.key?(:from)
258
268
  into = dir[:into] if dir.key?(:into)
@@ -265,10 +275,12 @@ module Squared
265
275
  glob = dir[:include]
266
276
  pass = dir[:exclude]
267
277
  dest = items.first unless dest && dest != true
278
+ @workspace.rev_clear(dest) unless dest == true
268
279
  when Project::Base
269
280
  dest = dir.path
281
+ @workspace.rev_clear(dir.name)
270
282
  else
271
- raise_error "copy given: #{dir}"
283
+ raise_error "copy: given #{dir}"
272
284
  end
273
285
  next unless from && dest&.directory?
274
286
 
@@ -318,7 +330,7 @@ module Squared
318
330
  if @depend && !flag
319
331
  super
320
332
  elsif outdated?
321
- workspace.rev_clear name
333
+ workspace.rev_clear(name)
322
334
  return update if !flag && env('NODE_UPDATE')
323
335
 
324
336
  if (yarn = dependtype(:yarn)) > 0
@@ -579,54 +591,55 @@ module Squared
579
591
  end
580
592
 
581
593
  def package(flag, opts = [])
594
+ workspace.rev_clear(name)
582
595
  if (yarn = dependtype(:yarn)) > 0
583
596
  cmd = session 'yarn', if flag == :update
584
597
  flag = yarn == 1 ? 'upgrade' : 'up'
585
598
  else
586
599
  flag
587
600
  end
588
- out = option_sanitize(opts, if yarn == 1
589
- OPT_PNPM[:install_base] + OPT_YARN.fetch(flag, []) + OPT_YARN[:common]
590
- else
591
- OPT_BERRY[flag]
592
- end).first
601
+ op = OptionPartition.new(opts, if yarn == 1
602
+ OPT_PNPM[:install_base] + OPT_YARN.fetch(flag, []) + OPT_YARN[:common]
603
+ else
604
+ OPT_BERRY[flag]
605
+ end, cmd, project: self)
606
+ op.clear
593
607
  append_loglevel
594
- option_clear out
595
608
  run(from: :"package:#{flag}")
596
609
  return
597
610
  elsif pnpm?
598
611
  cmd = session 'pnpm', flag
599
612
  list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
600
- list += OPT_PNPM[:install_as] unless flag == :dedupe
601
- opts = option_sanitize(opts, list, no: OPT_PNPM[:"#{flag}_no"]).first
613
+ list.concat(OPT_PNPM[:install_as]) unless flag == :dedupe
614
+ no = OPT_PNPM[:"#{flag}_no"]
602
615
  else
603
616
  cmd = session 'npm', flag
604
617
  list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
605
- list += OPT_NPM[:install_as] unless flag == :dedupe
606
- opts, pat = option_sanitize(opts, list, no: OPT_NPM[:install_no])
618
+ list.concat(OPT_NPM[:install_as]) unless flag == :dedupe
619
+ no = OPT_NPM[:install_no]
607
620
  end
608
- out = []
609
- err = []
610
- opts.each do |opt|
611
- if pat && opt =~ pat
621
+ op = OptionPartition.new(opts, list, cmd, no: no, project: self)
622
+ op.each do |opt|
623
+ if opt =~ op.values
612
624
  case $1
613
625
  when 'w', 'workspace'
614
- cmd << (%r{[\\/]}.match?($2) ? quote_option($1, basepath($2)) : shell_option($1, $2))
626
+ op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
615
627
  end
616
628
  elsif opt.include?('=')
617
- err << opt
629
+ op.errors << opt
618
630
  else
619
- out << opt
631
+ op.found << opt
620
632
  end
621
633
  end
634
+ op.swap
622
635
  append_nocolor
623
636
  append_loglevel
624
637
  if flag == :dedupe
625
- option_clear out
638
+ op.clear
626
639
  else
627
- append_value(out, escape: true)
640
+ op.append(escape: true)
628
641
  end
629
- option_clear err
642
+ op.clear(errors: true)
630
643
  run(from: :"package:#{flag}")
631
644
  end
632
645
 
@@ -680,7 +693,8 @@ module Squared
680
693
  end
681
694
  rescue StandardError => e
682
695
  log.debug e
683
- raise if exception
696
+ ret = on(:error, :bump, e)
697
+ raise if exception && ret != true
684
698
  end
685
699
  end
686
700
 
@@ -689,13 +703,25 @@ module Squared
689
703
 
690
704
  cmd = session dependbin, 'pack'
691
705
  if dependtype(:yarn) > 1
692
- out = option_sanitize(opts, OPT_BERRY[:pack]).first
693
- cmd << quote_option('out', Pathname.pwd.join("#{project}-#{version}.tgz")) unless session_arg?('out')
706
+ op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
707
+ op << quote_option('out', Pathname.pwd.join("#{project}-#{version}.tgz")) unless op.arg?('out')
694
708
  else
695
- out = option_sanitize(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common]).first
696
- cmd << quote_option('pack-destination', Dir.pwd) unless session_arg?('pack-destination')
709
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : OPT_NPM[:pack] + OPT_NPM[:common], cmd,
710
+ project: self)
711
+ unless pnpm?
712
+ op.each do |opt|
713
+ next unless opt =~ op.values
714
+
715
+ case $1
716
+ when 'w', 'workspace'
717
+ op << ($2.match?(%r{[\\/]}) ? quote_option($1, basepath($2)) : shell_option($1, $2))
718
+ op.found << opt
719
+ end
720
+ end
721
+ end
722
+ op << quote_option('pack-destination', Dir.pwd) unless op.arg?('pack-destination')
697
723
  end
698
- option_clear out
724
+ op.clear
699
725
  run(from: :pack)
700
726
  end
701
727
 
@@ -718,7 +744,7 @@ module Squared
718
744
  when Enumerable
719
745
  target.to_a.join(' ')
720
746
  else
721
- raise_error("compose given: #{target}", hint: from)
747
+ raise_error("compose: given #{target}", hint: from)
722
748
  end
723
749
  end
724
750
  end
@@ -4,9 +4,8 @@ module Squared
4
4
  module Workspace
5
5
  module Project
6
6
  class Python < Git
7
- REQUIREMENTS = %w[requirements.txt poetry.lock pyproject.toml setup.cfg].freeze
8
- SETUPTOOLS = %w[setup.py pyproject.toml].freeze
9
- DIR_PYTHON = (REQUIREMENTS + SETUPTOOLS).freeze
7
+ DEP_PYTHON = %w[poetry.lock setup.cfg pyproject.toml setup.py requirements.txt].freeze
8
+ DIR_PYTHON = (DEP_PYTHON + %w[README.rst]).freeze
10
9
  OPT_PYTHON = {
11
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,
12
11
  build: %w[n|no-isolation s|sdist v|verbose w|wheel x|skip-dependency-check C|config-setting=q installer=b
@@ -21,7 +20,7 @@ module Squared
21
20
  install: %w[break-system-packages check-build-dependencies compile dry-run force-reinstall I|ignore-installed
22
21
  ignore-requires-python no-build-isolation no-clean no-compile no-deps no-index no-warn-conflicts
23
22
  no-warn-script-location pre prefer-binary require-hashes U|upgrade use-pep517 user abi=b
24
- config-settings=q c|constraint=p e|editable=b? extra-index-url=q f|find-links=q global-option=q
23
+ config-settings=q c|constraint=p e|editable=v extra-index-url=q f|find-links=q global-option=q
25
24
  implementation=b i|index-url=q no-binary=q only-binary=q platform=q prefix=p progress-bar=b
26
25
  python-version=q report=p r|requirement=p root=p root-user-action=b src=p t|target=p
27
26
  upgrade-strategy=b].freeze,
@@ -46,8 +45,7 @@ module Squared
46
45
  config-file=p cert=p client-cert=p i|identity=b p|password=q r|repository=b repository-url=q
47
46
  sign-with=b u|username=q].freeze
48
47
  }.freeze
49
- private_constant :REQUIREMENTS, :SETUPTOOLS, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_HATCH,
50
- :OPT_TWINE
48
+ private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_HATCH, :OPT_TWINE
51
49
 
52
50
  class << self
53
51
  def populate(*); end
@@ -71,9 +69,9 @@ module Squared
71
69
  end
72
70
  end
73
71
 
74
- attr_reader :venv
72
+ attr_reader :venv, :editable
75
73
 
76
- def initialize(*, venv: nil, **kwargs)
74
+ def initialize(*, venv: nil, editable: '.', verbose: nil, **kwargs)
77
75
  super
78
76
  if @pass.include?(Python.ref)
79
77
  initialize_ref Python.ref
@@ -82,13 +80,20 @@ module Squared
82
80
  initialize_build(Python.ref, **kwargs)
83
81
  initialize_env(**kwargs)
84
82
  end
85
- @dependindex = REQUIREMENTS.index { |file| basepath(file).exist? }
86
- @dependfile = basepath(REQUIREMENTS[@dependindex || 0])
83
+ @dependindex = DEP_PYTHON.index { |file| basepath(file).exist? }
84
+ @dependfile = basepath(DEP_PYTHON[@dependindex || 0])
85
+ @verbose = $1.size if verbose.is_a?(String) && verbose =~ /^(v+)$/
86
+ @editable = case editable
87
+ when '.', Pathname
88
+ editable
89
+ when String
90
+ Pathname.new(editable)
91
+ end
87
92
  venv_set venv if venv
88
93
  end
89
94
 
90
95
  subtasks({
91
- 'venv' => %i[create remove show].freeze,
96
+ 'venv' => %i[run create remove show].freeze,
92
97
  'pip' => %i[uninstall freeze].freeze,
93
98
  'install' => %i[user force upgrade target editable].freeze,
94
99
  'build' => %i[python poetry hatch].freeze,
@@ -114,13 +119,14 @@ module Squared
114
119
  if flag == :create
115
120
  format_desc action, flag, 'dir,opts*'
116
121
  task flag, [:dir] do |_, args|
117
- dir = param_guard(action, flag, args: args, key: :dir)
118
- opts = python_session('-m venv', opts: args.extras).last
119
- opts = option_sanitize(opts, OPT_PYTHON[:venv]).first
120
- append_value(dir = basepath(dir), delim: true)
121
- option_clear(opts, pass: false)
122
+ dir = basepath(param_guard(action, flag, args: args, key: :dir))
123
+ cmd, opts = python_session('-m venv', opts: args.extras)
124
+ op = OptionPartition.new(opts, OPT_PYTHON[:venv], cmd, project: self)
125
+ op.append(dir, delim: true)
126
+ .clear(pass: false)
127
+ v = op.arg?(/^-v+$/, 'verbose')
122
128
  run(exception: true)
123
- puts(dir.directory? ? "Success: #{dir}" : 'Failed')
129
+ puts(dir.directory? ? "Success: #{dir}" : 'Failed') unless v
124
130
  end
125
131
  elsif venv
126
132
  case flag
@@ -129,10 +135,17 @@ module Squared
129
135
 
130
136
  format_desc action, flag, 'c|reate?,d|epend?'
131
137
  task flag do |_, args|
132
- venv.rmtree(verbose: true)
138
+ rm_rf(venv, verbose: true)
133
139
  venv_init if has_value?(%w[c create], args.to_a)
134
140
  depend if has_value?(%w[d depend], args.to_a)
135
141
  end
142
+ when :run
143
+ format_desc action, flag, 'args+'
144
+ task flag do |_, args|
145
+ args = param_guard(action, flag, args: args.to_a)
146
+ venv_init
147
+ run session(*args, path: false)
148
+ end
136
149
  when :show
137
150
  format_desc action, flag
138
151
  task flag do
@@ -143,7 +156,7 @@ module Squared
143
156
  when 'pip'
144
157
  case flag
145
158
  when :freeze
146
- format_desc action, flag, 'file?=requirements.txt,opts*'
159
+ format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
147
160
  task flag do |_, args|
148
161
  if (file = pip(flag, args.to_a)) && verbose
149
162
  puts File.read(file)
@@ -162,7 +175,7 @@ module Squared
162
175
  when :editable
163
176
  'path/url?'
164
177
  when :upgrade
165
- 'eager?,package+'
178
+ 'strategy?,package+'
166
179
  end)
167
180
  case flag
168
181
  when :editable
@@ -222,10 +235,9 @@ module Squared
222
235
  super
223
236
  elsif outdated?
224
237
  venv_init
225
- workspace.rev_clear name
226
- if !flag && dependtype == 2
227
- cmd = session 'poetry', 'install', '-n'
228
- cmd << '--no-ansi' unless verbose
238
+ workspace.rev_clear(name)
239
+ if !flag && dependtype == 1
240
+ cmd = poetry_session 'install', '-n'
229
241
  cmd << '--no-root' if option('no-root')
230
242
  else
231
243
  cmd = pip_session 'install'
@@ -240,16 +252,10 @@ module Squared
240
252
  end
241
253
  append_pip(flag, opts, from: :install)
242
254
  else
243
- if (val = option('editable', 'e'))
244
- cmd << quote_option('e', editable(val))
245
- end
246
255
  append_global
247
256
  end
248
- if dependtype == 1 && !session_arg?('e', 'editable')
249
- cmd << '-r requirements.txt' unless session_arg?('r', 'requirement')
250
- elsif !session_arg?('e', 'editable', value: true)
251
- cmd << '.'
252
- end
257
+ cmd << "-r #{DEP_PYTHON[4]}" if basepath(DEP_PYTHON[4]).exist? && !session_arg?('r', 'requirement')
258
+ append_editable
253
259
  end
254
260
  run(from: :depend, sync: sync)
255
261
  end
@@ -333,9 +339,10 @@ module Squared
333
339
  out = append_pip(flag, opts, from: :install)
334
340
  case flag
335
341
  when :editable
336
- cmd << quote_option('e', out.shift || '.')
342
+ cmd << quote_option('e', out.pop || editable || '.')
337
343
  option_clear out
338
344
  when :upgrade
345
+ raise_error('no packages listed', hint: flag) if out.empty?
339
346
  cmd << '--upgrade'
340
347
  cmd << basic_option('upgrade-strategy', strategy) if strategy
341
348
  append_value out
@@ -344,33 +351,34 @@ module Squared
344
351
  end
345
352
 
346
353
  def buildx(flag, opts = [])
347
- out = []
348
- srcdir = nil
349
354
  case flag
350
355
  when :python
351
356
  cmd, opts = python_session('-m build', opts: opts)
352
357
  list = OPT_PYTHON[:build]
353
358
  when :poetry
354
- cmd = session 'poetry', 'build'
359
+ cmd = poetry_session 'build'
355
360
  list = OPT_POETRY[:build] + OPT_POETRY[:common]
356
361
  when :hatch
357
362
  cmd, opts = hatch_session('build', opts: opts)
358
363
  list = OPT_HATCH[:build]
359
364
  end
360
- option_sanitize(opts, list, single: flag == :python ? /^(?:v+|q+|b+)$/ : /^(?:v+|q+)$/).first.each do |opt|
365
+ srcdir = nil
366
+ op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
367
+ op.each do |opt|
361
368
  if !srcdir && basepath(opt).exist? && projectpath?(opt)
362
369
  srcdir = opt
363
370
  else
364
- out << opt
371
+ op.found << opt
365
372
  end
366
373
  end
374
+ op.swap
367
375
  case flag
368
376
  when :poetry
369
377
  if srcdir
370
- if session_arg?('o', 'output')
371
- out << srcdir
378
+ if op.arg?('o', 'output')
379
+ op.extras << srcdir
372
380
  else
373
- cmd << quote_option('output', basepath(srcdir))
381
+ op << quote_option('output', basepath(srcdir))
374
382
  end
375
383
  srcdir = nil
376
384
  end
@@ -380,33 +388,32 @@ module Squared
380
388
  else
381
389
  srcdir ||= path
382
390
  end
383
- cmd << basic_option('p', project) unless ENV['HATCH_PROJECT'] || session_arg?('p', 'project')
391
+ op << basic_option('p', project) unless ENV['HATCH_PROJECT'] || op.arg?('p', 'project')
384
392
  end
385
- cmd << shell_quote(basepath(srcdir)) if srcdir
386
- option_clear out
393
+ op << shell_quote(basepath(srcdir)) if srcdir
394
+ op.clear
387
395
  run(from: :"#{flag}:build")
388
396
  end
389
397
 
390
398
  def publish(flag, opts = [])
391
- cmd = session flag
392
399
  case flag
393
400
  when :poetry
394
- cmd << 'publish'
401
+ poetry_session 'publish'
395
402
  list = OPT_POETRY[:publish] + OPT_POETRY[:common]
396
403
  when :twine
397
- cmd << 'upload'
404
+ session 'twine', 'upload'
398
405
  list = OPT_TWINE[:publish]
399
406
  when :hatch
400
407
  opts = hatch_session('publish', opts: opts).last
401
408
  list = OPT_HATCH[:publish]
402
409
  end
403
- out = option_sanitize(opts, list, single: flag == :twine ? nil : /^(?:v+|q+)$/).first
404
- if out.empty?
410
+ op = OptionPartition.new(opts, list, @session, project: self, single: singleopt(flag))
411
+ if op.empty?
405
412
  dist = basepath.join('dist')
406
- raise_error('no source files given', hint: dist) unless dist.directory? && !dist.empty?
407
- out << "#{dist}/*" unless flag == :poetry
413
+ raise_error('no source files found', hint: dist) unless dist.directory? && !dist.empty?
414
+ op.extras << "#{dist}/*" unless flag == :poetry
408
415
  end
409
- append_value out
416
+ op.append
410
417
  run(from: :"#{flag}:publish")
411
418
  end
412
419
 
@@ -415,11 +422,11 @@ module Squared
415
422
  out = append_pip(nil, opts, from: flag)
416
423
  case flag
417
424
  when :uninstall
418
- raise_error('no packages given', subject: name, hint: 'uninstall') if out.empty?
425
+ raise_error('no packages listed', hint: 'uninstall') if out.empty?
419
426
  cmd.merge(out)
420
427
  when :freeze
421
428
  venv_init
422
- ret = basepath(out.shift || 'requirements.txt')
429
+ ret = basepath(out.shift || DEP_PYTHON[4])
423
430
  cmd << '>' << shell_quote(ret)
424
431
  option_clear out
425
432
  end
@@ -431,14 +438,14 @@ module Squared
431
438
  case key
432
439
  when :dependfile
433
440
  req = basepath(*val)
434
- if (index = REQUIREMENTS.index(req.basename.to_s))
441
+ if (index = DEP_PYTHON.index(req.basename.to_s))
435
442
  @dependindex = index
436
443
  @dependfile = req
437
444
  else
438
445
  log.warn "variable_set: @#{key}=#{req} (not supported)"
439
446
  end
440
- when :venv
441
- @venv = val.empty? ? nil : basepath(*val)
447
+ when :venv, :editable
448
+ instance_variable_set(:"@#{key}", val.empty? ? nil : basepath(*val))
442
449
  else
443
450
  super
444
451
  end
@@ -455,62 +462,69 @@ module Squared
455
462
  private
456
463
 
457
464
  def pip_session(*cmd)
458
- session('pip', *cmd, path: venv.nil?)
465
+ session('pip', *cmd, *preopts, path: venv.nil?)
459
466
  end
460
467
 
461
468
  def python_session(*cmd, opts: nil)
462
- return session('python', *cmd, path: venv.nil?) unless opts
469
+ return session('python', *preopts(quiet: false), *cmd, path: venv.nil?) unless opts
463
470
 
464
- out = []
465
- opts = option_sanitize(opts, OPT_PYTHON[:common], target: out, single: /^v+$/).first
466
- ret = session('python', *out, *cmd, path: venv.nil?)
467
- [ret, opts]
471
+ op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: /^v+$/)
472
+ ret = session('python', *op.to_a, *cmd, path: venv.nil?)
473
+ [ret, op.extras]
474
+ end
475
+
476
+ def poetry_session(*cmd)
477
+ ret = session('poetry', *cmd, *preopts)
478
+ if (val = option('project', ignore: false))
479
+ ret << quote_option('project', basepath(val))
480
+ end
481
+ ret
468
482
  end
469
483
 
470
484
  def hatch_session(*cmd, opts: nil)
471
- return session('hatch', *cmd, path: venv.nil?) unless opts
485
+ return session('hatch', *preopts, *cmd, path: venv.nil?) unless opts
472
486
 
473
- out = []
474
- opts = option_sanitize(opts, OPT_HATCH[:common], target: out, single: /^(?:v+|q+)$/).first
475
- ret = session('hatch', *out, *cmd, path: venv.nil?)
476
- [ret, opts]
487
+ op = OptionPartition.new(opts, OPT_HATCH[:common], project: self, single: singleopt)
488
+ ret = session('hatch', *op.to_a, *cmd, path: venv.nil?)
489
+ [ret, op.extras]
477
490
  end
478
491
 
479
- def append_pip(flag, opts, from: nil)
492
+ def append_pip(flag, opts, target: @session, from: nil)
480
493
  if !from || opts.empty?
481
- append_global
494
+ append_global(target: target)
482
495
  return []
483
496
  end
484
- opts, pat = option_sanitize(opts, OPT_PIP[from] + OPT_PIP[:common], single: /^(?:v+|q+)$/)
485
- append_global
497
+ op = OptionPartition.new(opts, OPT_PIP[from] + OPT_PIP[:common], target, project: self, single: singleopt)
498
+ append_global(target: target)
486
499
  if from == :install
487
- out = []
488
500
  edit = nil
489
- opts.each do |opt|
490
- if opt =~ pat
501
+ op.each do |opt|
502
+ if opt =~ op.values
491
503
  case $1
492
504
  when 'e', 'editable'
505
+ op.found << edit if edit && flag == :editable
493
506
  edit = $2
494
507
  end
495
508
  elsif flag == :editable && !edit
496
509
  edit = opt
497
510
  else
498
- out << opt
511
+ op.found << opt
499
512
  end
500
513
  end
514
+ op.swap
501
515
  if edit
502
- edit = editable(edit)
516
+ edit = basepath(edit) unless %r{^[a-z]+(?:\+[a-z]+)?://}i.match?(edit)
503
517
  if flag == :editable
504
- out << edit
518
+ op.extras << edit
505
519
  else
506
520
  target << quote_option('e', edit)
507
521
  end
508
522
  end
509
523
  case flag
510
524
  when :editable, :upgrade
511
- out
525
+ op.extras
512
526
  else
513
- option_clear out
527
+ op.clear
514
528
  []
515
529
  end
516
530
  else
@@ -518,8 +532,25 @@ module Squared
518
532
  end
519
533
  end
520
534
 
535
+ def append_editable(target: @session)
536
+ return if requirements?
537
+
538
+ if (val = option('editable', 'e', target: target, ignore: false))
539
+ session_delete('e', 'editable', target: target)
540
+ case val
541
+ when '0', 'false'
542
+ return
543
+ else
544
+ val = basepath(val)
545
+ end
546
+ elsif session_arg?('e', 'editable', target: target) || !(val = editable)
547
+ return
548
+ end
549
+ target << quote_option('e', val)
550
+ end
551
+
521
552
  def append_global(target: @session)
522
- if (val = option('cache-dir'))
553
+ if (val = option('cache-dir', target: target))
523
554
  target << case val
524
555
  when '0', 'false'
525
556
  '--no-cache-dir'
@@ -527,17 +558,35 @@ module Squared
527
558
  quote_option('cache-dir', basepath(val))
528
559
  end
529
560
  end
530
- target << shell_option('proxy', val) if (val = option('proxy'))
531
- target << quote_option('python', basepath(val)) if (val = option('python'))
561
+ target << shell_option('proxy', val) if (val = option('proxy', target: target))
562
+ target << quote_option('python', basepath(val)) if (val = option('python', target: target))
532
563
  append_nocolor(target: target)
533
564
  end
534
565
 
535
- def editable(val)
536
- %r{^[a-z]+(?:\+[a-z]+)?://}i.match?(val) ? val : basepath(val)
566
+ def singleopt(flag = nil)
567
+ case flag
568
+ when :python
569
+ /^(?:v+|q+|b+)$/
570
+ when :twine
571
+ nil
572
+ else
573
+ /^(?:v+|q+)$/
574
+ end
575
+ end
576
+
577
+ def preopts(quiet: true)
578
+ ret = []
579
+ case verbose
580
+ when false
581
+ ret << '--quiet' if quiet
582
+ when Numeric
583
+ ret << "-#{'v' * verbose}" if verbose > 0
584
+ end
585
+ ret
537
586
  end
538
587
 
539
588
  def variables
540
- (super + %i[venv]).freeze
589
+ (super + %i[venv editable]).freeze
541
590
  end
542
591
 
543
592
  def runenv
@@ -551,7 +600,7 @@ module Squared
551
600
  end
552
601
 
553
602
  def venvbin
554
- @venv.join(workspace.windows? ? 'Scripts' : 'bin')
603
+ @venv&.join(workspace.windows? ? 'Scripts' : 'bin')
555
604
  end
556
605
 
557
606
  def venv_set(val)
@@ -574,10 +623,14 @@ module Squared
574
623
  return if !venv || (venvbin.directory? && !venvbin.empty?)
575
624
 
576
625
  puts log_message(Logger::INFO, venv, subject: 'venv', hint: 'init')
577
- cmd = python_session '-m venv', shell_option('prompt', name), '--upgrade-deps', shell_quote(venv)
626
+ cmd = session 'python', '-m venv', shell_option('prompt', name), '--upgrade-deps', shell_quote(venv)
578
627
  run(cmd, false, exception: true, banner: false)
579
628
  puts log_message(Logger::INFO, venv, subject: 'venv', hint: 'created')
580
629
  end
630
+
631
+ def requirements?
632
+ dependtype == 5
633
+ end
581
634
  end
582
635
 
583
636
  Application.implement Python