squared 0.4.11 → 0.4.13

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.
@@ -11,10 +11,13 @@ module Squared
11
11
  install_no: %w[audit bin-links fund package-lock].freeze,
12
12
  install_as: %w[foreground-scripts g|global no-save save save-bundle save-dev E|save-exact save-optional
13
13
  save-peer S|save-prod].freeze,
14
+ run: %w[foreground-scripts if-present ignore-scripts script-shell=p].freeze,
15
+ exec: %w[c|call=q package=b].freeze,
14
16
  pack: %w[json ignore-scripts pack-destination=p].freeze
15
17
  }.freeze
16
18
  OPT_PNPM = {
17
19
  common: %w[aggregate-output color no-color stream use-stderr C|dir=p loglevel=b w|workspace-root].freeze,
20
+ filter: %w[fail-if-no-match changed-files-ignore-pattern=q filter=q filter-prod=q test-pattern=q].freeze,
18
21
  install: %w[fix-lockfile force ignore-pnpmfile ignore-workspace lockfile-only merge-git-branch-lockfiles
19
22
  no-hoist no-lockfile no-optional prefer-frozen-lockfile resolution-only shamefully-hoist
20
23
  side-effects-cache side-effects-cache-readonly s|silent strict-peer-dependencies
@@ -23,28 +26,32 @@ module Squared
23
26
  reporter=b].freeze,
24
27
  install_base: %w[global-dir ignore-scripts offline prefer-offline store-dir=p virtual-store-dir=p].freeze,
25
28
  install_no: %w[frozen-lockfile verify-store-integrity].freeze,
26
- install_as: %w[D|dev fail-if-no-match global-dir no-optional P|prod r|recursive
27
- changed-files-ignore-pattern=q filter=q filter-prod=q test-pattern=q].freeze,
29
+ install_as: %w[D|dev global-dir no-optional P|prod r|recursive].freeze,
28
30
  update: %w[global interactive latest depth=i].freeze,
29
31
  dedupe: %w[check].freeze,
32
+ run: %w[if-present no-bail parallel r|recursive report-summary reporter-hide-prefix resume-from sequential
33
+ stream use-stderr].freeze,
34
+ exec: %w[no-reporter-hide-prefix parallel r|recursive report-summary resume-from c|shell-mode].freeze,
30
35
  pack: %w[json pack-destination=p pack-gzip-level=i].freeze
31
36
  }.freeze
32
37
  OPT_YARN = {
33
- common: %w[no-default-rc s|silent verbose cwd=p use-yarnrc=p].freeze,
38
+ common: %w[check-files disable-pnp enable-pnp flat focus force frozen-lockfile json har ignore-engines
39
+ ignore-optional ignore-platform ignore-scripts link-duplicates no-bin-links no-default-rc
40
+ no-lockfile no-node-version-check no-progress non-interactive offline pnp prefer-offline prod
41
+ pure-lockfile s|silent skip-integrity-check strict-semver verbose cache-folder=p cwd=p emoji=b?
42
+ global-folder=p https-proxy=q link-folder=p modules-folder=p mutex=q network-concurrency=i
43
+ network-timeout=i preferred-cache-folder=p production=b? proxy=q otp=b registry=q update-checksums
44
+ use-yarnrc=p].freeze,
34
45
  install: %w[A|audit g|global S|save D|save-dev E|save-exact P|save-peer O|save-optional T|save-tilde].freeze,
35
- install_base: %w[check-files disable-pnp enable-pnp flat focus force frozen-lockfile har ignore-platform
36
- ignore-engines ignore-optional ignore-scripts json link-duplicates no-bin-links no-lockfile
37
- no-node-version-check no-progress non-interactive offline pnp prefer-offline prod
38
- pure-lockfile skip-integrity-check strict-semver update-checksums cache-folder=p emoji=b?
39
- global-folder=p https-proxy=q link-folder=p modules-folder=p mutex=q network-concurrency=i
40
- network-timeout=i preferred-cache-folder=p production=b? proxy=q otp=b registry=q].freeze,
41
- update: %w[A|audit C|caret E|exact L|latest T|tilde P|pattern=q S|scope=b].freeze
46
+ update: %w[A|audit C|caret E|exact L|latest T|tilde P|pattern=q S|scope=b].freeze,
47
+ run: %w[scripts-prepend-node-path=b?].freeze
42
48
  }.freeze
43
49
  OPT_BERRY = {
44
50
  install: %w[check-cache check-resolutions immutable immutable-cache inline-builds json refresh-lockfile
45
51
  mode=b].freeze,
46
52
  update: %w[C|caret E|exact F|fixed interactive T|tilde R|recursive mode=b].freeze,
47
53
  dedupe: %w[check json mode=b strategy=b].freeze,
54
+ run: %w[B|binaries-only inspect inspect-brk T|top-level require=q].freeze,
48
55
  pack: %w[n|dry-run install-if-needed json o|out=p].freeze
49
56
  }.freeze
50
57
  private_constant :OPT_NPM, :OPT_PNPM, :OPT_YARN, :OPT_BERRY
@@ -86,6 +93,7 @@ module Squared
86
93
  'publish' => %i[latest tag].freeze,
87
94
  'add' => nil,
88
95
  'run' => nil,
96
+ 'exec' => nil,
89
97
  'pack' => nil
90
98
  })
91
99
 
@@ -98,8 +106,8 @@ module Squared
98
106
  initialize_build(Node.ref, prod: prod?, **kwargs)
99
107
  initialize_env(**kwargs)
100
108
  end
101
- @pm = {}
102
109
  @dependfile = @path + 'package.json'
110
+ @pm = {}
103
111
  end
104
112
 
105
113
  def ref
@@ -137,25 +145,70 @@ module Squared
137
145
  when 'run'
138
146
  next if (list = read_scripts).empty?
139
147
 
140
- format_desc action, nil, 'command+|^index|#,pattern*'
141
- task action, [:command] do |_, args|
142
- if args.command == '#'
148
+ format_desc action, nil, 'script,opts*|^index+|#,pattern*'
149
+ task action, [:script] do |_, args|
150
+ if args.script == '#'
143
151
  format_list(list, 'run[^N]', 'scripts', grep: args.extras, from: dependfile.to_s)
144
152
  else
145
- cmd = param_guard(action, 'command', args: args.to_a)
146
- cmd.each do |val|
147
- if (n, opts = indexitem(val))
153
+ args = param_guard(action, 'script', args: args.to_a)
154
+ opts = []
155
+ args.each do |val|
156
+ if (n, extra = indexitem(val))
148
157
  if (item = list[n - 1])
149
- val = opts ? "#{item.first} #{opts}" : item.first
158
+ val = extra ? "#{item.first} #{extra}" : item.first
150
159
  elsif exception
151
160
  indexerror n, list
152
161
  else
153
162
  next log.warn "run script #{n} of #{list.size} (out of range)"
154
163
  end
164
+ run compose(val, script: true)
165
+ else
166
+ opts << val
167
+ end
168
+ end
169
+ next if opts.empty?
170
+
171
+ list = if (yarn = dependtype(:yarn)) > 0
172
+ yarn == 1 ? OPT_YARN[:run] + OPT_YARN[:common] : OPT_BERRY[:run]
173
+ elsif pnpm?
174
+ OPT_PNPM[:run] + OPT_PNPM[:filter] + OPT_PNPM[:common]
175
+ else
176
+ OPT_NPM[:run] + OPT_NPM[:common]
177
+ end
178
+ op = OptionPartition.new(opts, list, session(dependbin, 'run'), project: self)
179
+ op << op.extras.shift
180
+ op.append(delim: true, quote: false)
181
+ run(from: :run)
182
+ end
183
+ end
184
+ when 'exec'
185
+ format_desc action, nil, 'pkg/cmd,opts*,args*'
186
+ task action, [:package] do |_, args|
187
+ if (package = args.package)
188
+ args = args.to_a.drop(1)
189
+ if pnpm?
190
+ pre = ->(ch) { "-#{ch}" if (ch = args.delete(ch)) }
191
+ cmd = session 'pnpm', pre.call('r'), pre.call('c'), 'exec'
192
+ list = OPT_PNPM[:exec] + OPT_PNPM[:filter] + OPT_PNPM[:common]
193
+ else
194
+ cmd = session 'npm', 'exec'
195
+ list = OPT_NPM[:exec] + OPT_NPM[:common]
196
+ end
197
+ op = OptionPartition.new(args, list, cmd, project: self)
198
+ if op.empty?
199
+ op << package
200
+ if (args = readline('Enter arguments', force: false))
201
+ op << '--' unless pnpm?
202
+ op << args
155
203
  end
156
- run compose(val, script: true)
204
+ else
205
+ op << '--' unless pnpm?
206
+ op << package << op.join(' ')
157
207
  end
208
+ else
209
+ session 'npm', 'exec', quote_option('c', readline('Enter command', force: true), double: true)
158
210
  end
211
+ run(from: :exec)
159
212
  end
160
213
  when 'pack'
161
214
  format_desc action, nil, 'opts*'
@@ -344,8 +397,7 @@ module Squared
344
397
  elsif pnpm?
345
398
  cmd = session 'pnpm'
346
399
  if flag == :add
347
- cmd << 'add'
348
- cmd << "--save-#{save}"
400
+ cmd << 'add' << "--save-#{save}"
349
401
  cmd << '--save-exact' if exact
350
402
  else
351
403
  cmd << 'install'
@@ -372,7 +424,7 @@ module Squared
372
424
  end
373
425
 
374
426
  def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
375
- dryrun = opts.include?('dry-run')
427
+ dryrun = opts.include?('dry-run') || opts.include?('d')
376
428
  if pnpm? && read_packagemanager(version: '7.15', update: true)
377
429
  cmd = session 'pnpm', 'outdated'
378
430
  dryrun ||= dryrun?('pnpm')
@@ -406,7 +458,7 @@ module Squared
406
458
  found = []
407
459
  avail = []
408
460
  rev = flag || (prod? ? :patch : :minor)
409
- inter = opts.include?('interactive')
461
+ inter = opts.include?('interactive') || opts.include?('i')
410
462
  unless data.empty?
411
463
  JSON.parse(data).each_pair do |key, val|
412
464
  val = val.find { |obj| obj['dependent'] == target } if val.is_a?(Array)
@@ -446,7 +498,7 @@ module Squared
446
498
  found << [key, file, want, if a != c
447
499
  1
448
500
  elsif b != d
449
- 3
501
+ a == '0' ? 1 : 3
450
502
  else
451
503
  5
452
504
  end, major, f, w]
@@ -479,8 +531,8 @@ module Squared
479
531
  col2 = size_col.call(found, 1) + 4
480
532
  found.each_with_index do |item, i|
481
533
  a, b, c, d, e = item
482
- inter &&= rev != :major || e || semmajor?(item[5], item[6])
483
- if inter && !confirm_outdated(a, c, (d / 2.0).ceil, lock: e)
534
+ f = inter && (rev != :major || e || semmajor?(item[5], item[6]))
535
+ if f && !confirm_outdated(a, c, (d / 2.0).ceil, lock: e)
484
536
  cur = -1
485
537
  else
486
538
  cur = modified
@@ -506,7 +558,7 @@ module Squared
506
558
  a = sub_style(a, styles: theme[:major])
507
559
  sub_style(c, :bold, styles: color(:green))
508
560
  else
509
- sub_style(c, pat: SEM_VER, styles: color(:green), index: d)
561
+ sub_style(c, pat: SEM_VER, styles: color(d == 3 ? :green : :yellow), index: d)
510
562
  end
511
563
  puts "#{pad_ord.call(i, found)}. #{a + b + c}"
512
564
  end
@@ -518,7 +570,7 @@ module Squared
518
570
  footer.call(0, found.size)
519
571
  File.write(dependfile, doc)
520
572
  commit(:add, refs: ['package.json'], pass: true)
521
- install if opts.include?('prune')
573
+ install if opts.include?('prune') || opts.include?('p')
522
574
  end
523
575
  elsif !avail.empty?
524
576
  col1 = size_col.call(avail, 0) + 4
@@ -596,47 +648,47 @@ module Squared
596
648
  flag
597
649
  end
598
650
  op = OptionPartition.new(opts, if yarn == 1
599
- OPT_PNPM[:install_base] + OPT_YARN.fetch(flag, []) + OPT_YARN[:common]
651
+ OPT_YARN.fetch(flag, []) + OPT_YARN[:common]
600
652
  else
601
653
  OPT_BERRY[flag]
602
654
  end, cmd, project: self)
603
655
  op.clear
604
656
  append_loglevel
605
- run(from: :"package:#{flag}")
606
- return
607
- elsif pnpm?
608
- cmd = session 'pnpm', flag
609
- list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
610
- list.concat(OPT_PNPM[:install_as]) unless flag == :dedupe
611
- no = OPT_PNPM[:"#{flag}_no"]
612
657
  else
613
- cmd = session 'npm', flag
614
- list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
615
- list.concat(OPT_NPM[:install_as]) unless flag == :dedupe
616
- no = OPT_NPM[:install_no]
617
- end
618
- op = OptionPartition.new(opts, list, cmd, no: no, project: self)
619
- op.each do |opt|
620
- if opt =~ op.values
621
- case $1
622
- when 'w', 'workspace'
623
- op << ($2.match?(%r{[\\/]}) ? quote_option($1, path + $2) : shell_option($1, $2))
658
+ if pnpm?
659
+ cmd = session 'pnpm', flag
660
+ list = OPT_PNPM[:install_base] + OPT_PNPM.fetch(flag, []) + OPT_PNPM[:common]
661
+ list.concat(OPT_PNPM[:install_as] + OPT_PNPM[:filter]) unless flag == :dedupe
662
+ no = OPT_PNPM[:"#{flag}_no"]
663
+ else
664
+ cmd = session 'npm', flag
665
+ list = OPT_NPM[:install_base] + OPT_NPM.fetch(flag, []) + OPT_NPM[:common]
666
+ list.concat(OPT_NPM[:install_as]) unless flag == :dedupe
667
+ no = OPT_NPM[:install_no]
668
+ end
669
+ op = OptionPartition.new(opts, list, cmd, no: no, project: self)
670
+ op.each do |opt|
671
+ if opt =~ op.values
672
+ case $1
673
+ when 'w', 'workspace'
674
+ op << ($2.match?(%r{[\\/]}) ? quote_option($1, path + $2) : shell_option($1, $2))
675
+ end
676
+ elsif opt.include?('=')
677
+ op.errors << opt
678
+ else
679
+ op.found << opt
624
680
  end
625
- elsif opt.include?('=')
626
- op.errors << opt
681
+ end
682
+ op.swap
683
+ append_nocolor
684
+ append_loglevel
685
+ if flag == :dedupe
686
+ op.clear
627
687
  else
628
- op.found << opt
688
+ op.append(escape: true)
629
689
  end
690
+ op.clear(errors: true)
630
691
  end
631
- op.swap
632
- append_nocolor
633
- append_loglevel
634
- if flag == :dedupe
635
- op.clear
636
- else
637
- op.append(escape: true)
638
- end
639
- op.clear(errors: true)
640
692
  run(from: :"package:#{flag}")
641
693
  end
642
694
 
@@ -80,24 +80,19 @@ module Squared
80
80
  initialize_build(Python.ref, **kwargs)
81
81
  initialize_env(**kwargs)
82
82
  end
83
- @dependindex = DEP_PYTHON.index { |file| basepath(file).exist? }
84
- @dependfile = @path + DEP_PYTHON[@dependindex || 0]
83
+ dependfile_set DEP_PYTHON
85
84
  @verbose = verbose.size if verbose.is_a?(String) && verbose.match?(/\Av+\z/)
86
- @editable = case editable
87
- when '.', Pathname
88
- editable
89
- when String
90
- Pathname.new(editable)
91
- end
85
+ editable_set editable
92
86
  venv_set venv if venv
93
87
  end
94
88
 
95
89
  subtasks({
96
- 'venv' => %i[run create remove show].freeze,
90
+ 'venv' => %i[exec create remove show].freeze,
97
91
  'pip' => %i[uninstall freeze].freeze,
98
92
  'install' => %i[user force upgrade target editable].freeze,
99
93
  'build' => %i[python poetry hatch].freeze,
100
- 'publish' => %i[poetry twine hatch].freeze
94
+ 'publish' => %i[poetry twine hatch].freeze,
95
+ 'exec' => nil
101
96
  })
102
97
 
103
98
  def ref
@@ -112,111 +107,130 @@ module Squared
112
107
  Python.subtasks do |action, flags|
113
108
  next if @pass.include?(action)
114
109
 
115
- namespace action do
116
- flags.each do |flag|
117
- case action
118
- when 'venv'
119
- if flag == :create
120
- format_desc action, flag, 'dir,opts*'
121
- task flag, [:dir] do |_, args|
122
- dir = path + 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?(/\A-v+\z/, 'verbose')
128
- run(exception: true)
129
- puts(dir.directory? ? "Success: #{dir}" : 'Failed') unless v
110
+ if flags.nil?
111
+ case action
112
+ when 'exec'
113
+ format_desc action, nil, 'command|:,args*'
114
+ task action do |_, args|
115
+ i = (args = args.to_a).delete(':')
116
+ cmd = if i && !workspace.windows?
117
+ readline('Enter script', force: true, multiline: ['##', ';'])
118
+ elsif i || args.empty?
119
+ readline('Enter command', force: true)
120
+ else
121
+ if (val = command_args(args, prefix: 'python'))
122
+ args << val
123
+ end
124
+ args.join(' ')
125
+ end
126
+ Kernel.exec(cmd, chdir: path)
127
+ end
128
+ end
129
+ else
130
+ namespace action do
131
+ flags.each do |flag|
132
+ case action
133
+ when 'venv'
134
+ if flag == :create
135
+ format_desc action, flag, 'dir,opts*'
136
+ task flag, [:dir] do |_, args|
137
+ dir = path + param_guard(action, flag, args: args, key: :dir)
138
+ venv_create dir, args.extras
139
+ end
140
+ elsif venv
141
+ case flag
142
+ when :remove
143
+ next unless projectpath?(venv)
144
+
145
+ format_desc action, flag, 'c|create?,d|depend?'
146
+ task flag do |_, args|
147
+ rm_rf(venv, verbose: true)
148
+ venv_init if has_value?(%w[c create], args.to_a)
149
+ depend if has_value?(%w[d depend], args.to_a)
150
+ end
151
+ when :exec
152
+ format_desc action, flag, 'command,args*'
153
+ task flag do |_, args|
154
+ args = args.to_a
155
+ if args.empty?
156
+ args = readline('Enter command', force: true).split(' ', 2)
157
+ elsif args.size == 1 && !option('interactive', prefix: 'venv', equals: '0')
158
+ args << readline('Enter arguments', force: false)
159
+ end
160
+ venv_init
161
+ run args.join(' ')
162
+ end
163
+ when :show
164
+ format_desc action, flag
165
+ task flag do
166
+ puts venv
167
+ end
168
+ end
130
169
  end
131
- elsif venv
170
+ when 'pip'
132
171
  case flag
133
- when :remove
134
- next unless projectpath?(venv)
135
-
136
- format_desc action, flag, 'c|reate?,d|epend?'
172
+ when :freeze
173
+ format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
137
174
  task flag do |_, args|
138
- rm_rf(venv, verbose: true)
139
- venv_init if has_value?(%w[c create], args.to_a)
140
- depend if has_value?(%w[d depend], args.to_a)
175
+ if (file = pip(flag, args.to_a)) && verbose
176
+ puts File.read(file)
177
+ end
141
178
  end
142
- when :run
143
- format_desc action, flag, 'args+'
179
+ when :uninstall
180
+ format_desc action, flag, 'package+,opts*'
144
181
  task flag do |_, args|
145
- args = args.to_a
146
- args = readline('Enter command', force: true).split(' ', 2) if args.empty?
147
- venv_init
148
- run session(*args, path: false)
149
- end
150
- when :show
151
- format_desc action, flag
152
- task flag do
153
- puts venv
182
+ pip flag, args.to_a
154
183
  end
155
184
  end
156
- end
157
- when 'pip'
158
- case flag
159
- when :freeze
160
- format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
161
- task flag do |_, args|
162
- if (file = pip(flag, args.to_a)) && verbose
163
- puts File.read(file)
185
+ when 'install'
186
+ format_desc(action, flag, 'opts*', before: case flag
187
+ when :target then 'dir'
188
+ when :editable then 'path/url?'
189
+ when :upgrade then 'strategy?,package+'
190
+ end)
191
+ case flag
192
+ when :editable
193
+ task flag do |_, args|
194
+ install flag, args.to_a
164
195
  end
165
- end
166
- when :uninstall
167
- format_desc action, flag, 'package+,opts*'
168
- task flag do |_, args|
169
- pip flag, args.to_a
170
- end
171
- end
172
- when 'install'
173
- format_desc(action, flag, 'opts*', before: case flag
174
- when :target then 'dir'
175
- when :editable then 'path/url?'
176
- when :upgrade then 'strategy?,package+'
177
- end)
178
- case flag
179
- when :editable
180
- task flag do |_, args|
181
- install flag, args.to_a
182
- end
183
- when :upgrade
184
- task flag, [:strategy] do |_, args|
185
- case (strategy = args.strategy)
186
- when 'eager', 'only-if-needed'
187
- args = args.extras
188
- else
189
- args = args.to_a
190
- strategy = nil
196
+ when :upgrade
197
+ task flag, [:strategy] do |_, args|
198
+ case (strategy = args.strategy)
199
+ when 'eager', 'only-if-needed'
200
+ args = args.extras
201
+ else
202
+ args = args.to_a
203
+ strategy = nil
204
+ end
205
+ install(flag, args, strategy: strategy)
206
+ end
207
+ when :target
208
+ task flag, [:dir] do |_, args|
209
+ dir = param_guard(action, flag, args: args, key: :dir)
210
+ depend(flag, args.extras, target: dir)
211
+ end
212
+ else
213
+ task flag do |_, args|
214
+ depend flag, args.to_a
191
215
  end
192
- install(flag, args, strategy: strategy)
193
216
  end
194
- when :target
195
- task flag, [:dir] do |_, args|
196
- dir = param_guard(action, flag, args: args, key: :dir)
197
- depend(flag, args.extras, target: dir)
217
+ when 'build'
218
+ format_desc(action, flag, 'opts*', after: case flag
219
+ when :python then 'srcdir?'
220
+ when :hatch then 'location?'
221
+ end)
222
+ task flag do |_, args|
223
+ build! flag, args.to_a
198
224
  end
199
- else
225
+ when 'publish'
226
+ format_desc(action, flag, 'opts*', after: case flag
227
+ when :hatch then 'artifacts?'
228
+ when :twine then 'dist?'
229
+ end)
200
230
  task flag do |_, args|
201
- depend flag, args.to_a
231
+ publish flag, args.to_a
202
232
  end
203
233
  end
204
- when 'build'
205
- format_desc(action, flag, 'opts*', after: case flag
206
- when :python then 'srcdir?'
207
- when :hatch then 'location?'
208
- end)
209
- task flag do |_, args|
210
- build! flag, args.to_a
211
- end
212
- when 'publish'
213
- format_desc(action, flag, 'opts*', after: case flag
214
- when :hatch then 'artifacts?'
215
- when :twine then 'dist?'
216
- end)
217
- task flag do |_, args|
218
- publish flag, args.to_a
219
- end
220
234
  end
221
235
  end
222
236
  end
@@ -438,7 +452,9 @@ module Squared
438
452
  else
439
453
  log.warn "variable_set: @#{key}=#{req} (not supported)"
440
454
  end
441
- when :venv, :editable
455
+ when :editable
456
+ editable_set val.first
457
+ when :venv
442
458
  instance_variable_set(:"@#{key}", val.empty? ? nil : basepath(*val))
443
459
  else
444
460
  super
@@ -557,6 +573,15 @@ module Squared
557
573
  append_nocolor(target: target)
558
574
  end
559
575
 
576
+ def editable_set(val)
577
+ @editable = case val
578
+ when '.', Pathname
579
+ val
580
+ when String
581
+ Pathname.new(editable)
582
+ end
583
+ end
584
+
560
585
  def singleopt(flag = nil)
561
586
  case flag
562
587
  when :python
@@ -598,6 +623,10 @@ module Squared
598
623
  end
599
624
 
600
625
  def venv_set(val)
626
+ if val.is_a?(Array)
627
+ val, *opts = val
628
+ @venvopts = opts
629
+ end
601
630
  @venv = Pathname.new(val)
602
631
  @venv = @path + @venv unless @venv.absolute?
603
632
  if projectpath?(@venv)
@@ -617,11 +646,21 @@ module Squared
617
646
  return if !venv || (venvbin.directory? && !venvbin.empty?)
618
647
 
619
648
  puts log_message(Logger::INFO, venv, subject: 'venv', hint: 'init')
620
- cmd = session 'python', '-m venv', shell_option('prompt', name), '--upgrade-deps', shell_quote(venv)
621
- run(cmd, false, exception: true, banner: false)
649
+ @venvopts &&= @venvopts.map { |val| OptionPartition.strip(val) }.flatten
650
+ venv_create(venv, @venvopts || ["prompt=#{name}", 'upgrade-deps'], env: false, banner: false)
622
651
  puts log_message(Logger::INFO, venv, subject: 'venv', hint: 'created')
623
652
  end
624
653
 
654
+ def venv_create(dir, opts = [], env: nil, banner: true)
655
+ cmd, opts = python_session('-m venv', opts: opts)
656
+ op = OptionPartition.new(opts, OPT_PYTHON[:venv], cmd, project: self)
657
+ op.append(dir, delim: true)
658
+ .clear(pass: false)
659
+ status = op.arg?(/\A-v+\z/, 'verbose')
660
+ run(op, env, exception: true, banner: banner)
661
+ puts(dir.directory? ? "Success: #{dir}" : 'Failed') if banner && !status
662
+ end
663
+
625
664
  def requirements?
626
665
  dependtype == 5
627
666
  end