squared 0.6.10 → 0.7.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.
@@ -5,13 +5,18 @@ module Squared
5
5
  module Project
6
6
  class Python < Git
7
7
  DEP_PYTHON = %w[poetry.lock setup.cfg pyproject.toml setup.py requirements.txt].freeze
8
- DIR_PYTHON = (DEP_PYTHON + %w[README.rst]).freeze
8
+ DIR_PYTHON = (DEP_PYTHON + %w[meson.build 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=+ V=+ x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
11
11
  build: %w[C=bm n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel config-json=q config-setting=q
12
12
  installer=b o|outdir=p].freeze,
13
13
  venv: %w[clear copies symlinks system-site-packages upgrade upgrade-deps without-scm-ignore-files without-pip
14
- prompt=q].freeze
14
+ prompt=q].freeze,
15
+ virtualenv: %w[always-copy clear copies download q|quiet never-download no-download no-periodic-update no-pip
16
+ no-seed no-setuptools no-vcs-ignore read-only-app-data reset-app-data symlink-app-data symlinks
17
+ system-site-packages with-traceback without-pip upgrade-embed-wheels v|verbose=+ activators=q
18
+ app-data=p creator=b discovery=b extra-search-dir=p pip=b prompt=q p|python=q seeder=b
19
+ setuptools=b try-first-with=q].freeze
15
20
  }.freeze
16
21
  OPT_PIP = {
17
22
  common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input require-virtualenv
@@ -29,23 +34,23 @@ module Squared
29
34
  install: %w[break-system-packages compile dry-run force-reinstall I|ignore-installed no-compile
30
35
  no-warn-conflicts no-warn-script-location U|upgrade user prefix=p report=p root=p
31
36
  root-user-action=b t|target=p upgrade-strategy=b].freeze,
32
- install_a: %w[ignore-requires-python no-index pre prefer-binary all-releases=b extra-index-url=q
33
- f|find-links=q i|index-url=q no-binary=q only-binary=q only-final=b uploaded-prior-to=q].freeze,
34
- install_b: %w[build-constraint check-build-dependencies no-build-isolation no-clean no-deps
35
- require-hashes use-pep517 c|constraint=p group=q progress-bar=b r|requirement=p
36
- requirements-from-script=p src=p].freeze,
37
+ install_a: %w[ignore-requires-python no-index pre extra-index-url=q f|find-links=q i|index-url=q no-binary=q
38
+ only-binary=q].freeze,
39
+ install_b: %w[build-constraint check-build-dependencies no-build-isolation no-clean no-deps prefer-binary
40
+ require-hashes use-pep517 c|constraint=p group=q progress-bar=b r|requirement=p src=p].freeze,
37
41
  install_c: %w[C|config-settings=q e|editable=v].freeze,
38
42
  hash: %w[a|algorithm].freeze,
39
- list: %w[e|editable exclude-editable include-editable l|local no-index not-required o|outdated pre
40
- prefer-binary u|uptodate user all-releases=b exclude=b extra-index-url=q format=b f|find-links=q
41
- i|index-url=q no-binary=q only-binary=q only-final=b path=p].freeze,
43
+ list: %w[e|editable exclude-editable include-editable l|local no-index not-required o|outdated pre u|uptodate
44
+ user exclude=b extra-index-url=q format=b f|find-links=q i|index-url=q path=p].freeze,
42
45
  lock: %w[o|output=p].freeze,
43
46
  show: %w[f|files].freeze,
44
47
  uninstall: %w[break-system-packages y|yes r|requirement=p root-user-action=b].freeze,
45
48
  wheel: %w[no-verify w|wheel-dir=p].freeze
46
49
  }.freeze
47
50
  OPT_POETRY = {
48
- common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet=+ v|verbose=+ P|project=p].freeze,
51
+ common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose=+ P|project=p].freeze,
52
+ install: %w[all-extras all-groups compile dry-run no-directory no-root only-root E|extras=q only=q with=q
53
+ without=q].freeze,
49
54
  build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
50
55
  publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=q r|repository=q
51
56
  u|username=qq].freeze
@@ -69,13 +74,24 @@ module Squared
69
74
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
70
75
  sign-with=b u|username=qq].freeze
71
76
  }.freeze
77
+ OPT_MESON = {
78
+ build: %w[D=q clearcache debug errorlogs fatal-meson-warnings pkgconfig.relocatable prefer-static
79
+ python.allow-limited-api reconfigure stdsplit strip vsenv werror wipe auto-features=b backend=b
80
+ bindir=p build.cmake-prefix-path=p build.pkg-config-path=p buildtype=b cmake-prefix-path=p
81
+ cross-file=p datadir=p default-library=b default-both-libraries=b force-fallback-for=q genvslite=b
82
+ includedir=p infodir=p install-umask=b layout=b libdir=p libexecdir=p licensedir=p localedir=p
83
+ localstatedir=p mandir=p native-file=p optimization=b pkg-config-path=p prefix=p
84
+ python.bytecompile=i python.platlibdir=p python.purelibdir=p python.install-env=b sbindir=p
85
+ sharedstatedir=p sysconfdir=p unity=b unity-size=i warnlevel=b wrap-mode=b].freeze,
86
+ compile: %w[clean v|verbose j|jobs=i l|load-average=b ninja-args=q vs-args=q xcode-args=q].freeze
87
+ }.freeze
72
88
  PASS_PYTHON = {
73
89
  python: %w[c v V].freeze,
74
90
  pip: {
75
91
  debug: %w[platform].freeze,
76
92
  install: %w[C config-settings c constraint extra-index-url no-binary only-binary platform
77
93
  r requirement].freeze,
78
- list: %w[exclude extra-index-url no-binary only-binary].freeze
94
+ list: %w[exclude extra-index-url].freeze
79
95
  }.freeze
80
96
  }.freeze
81
97
  private_constant :DEP_PYTHON, :DIR_PYTHON, :OPT_PYTHON, :OPT_PIP, :OPT_POETRY, :OPT_PDM, :OPT_HATCH, :OPT_TWINE,
@@ -87,7 +103,7 @@ module Squared
87
103
  end
88
104
 
89
105
  def bannerargs
90
- %i[dependfile venv].freeze
106
+ %i[version dependfile venv].freeze
91
107
  end
92
108
 
93
109
  def venv?
@@ -101,14 +117,13 @@ module Squared
101
117
  end
102
118
  end
103
119
 
104
- attr_reader :venv
105
- attr_accessor :editable
120
+ attr_reader :venv, :editable
106
121
 
107
122
  def initialize(*, editable: '.', asdf: 'python', **kwargs)
108
123
  super
109
124
  if @pass.include?(Python.ref)
110
125
  initialize_ref Python.ref
111
- initialize_logger(**kwargs)
126
+ initialize_logger kwargs[:log]
112
127
  else
113
128
  initialize_build(Python.ref, **kwargs)
114
129
  initialize_env(**kwargs)
@@ -121,14 +136,18 @@ module Squared
121
136
  subtasks({
122
137
  'venv' => %i[exec create remove show].freeze,
123
138
  'pip' => %i[upgrade uninstall wheel reinstall freeze].freeze,
124
- 'install' => %i[user force upgrade target editable].freeze,
139
+ 'install' => %i[requirement target upgrade editable poetry].freeze,
125
140
  'outdated' => %i[major minor patch].freeze,
126
- 'build' => %i[poetry pdm hatch python].freeze,
141
+ 'build' => %i[poetry pdm hatch meson python].freeze,
127
142
  'publish' => %i[poetry pdm hatch twine].freeze,
128
143
  'run' => nil,
129
144
  'exec' => nil
130
145
  })
131
146
 
147
+ def project=(val)
148
+ @project = val.dup
149
+ end
150
+
132
151
  def verbose=(val)
133
152
  case val
134
153
  when /\Av+\z/
@@ -217,7 +236,8 @@ module Squared
217
236
  elsif i || args.empty?
218
237
  readline('Enter command', force: true)
219
238
  else
220
- (args << command_args(args, min: 1, prefix: 'python')).compact.join(' ')
239
+ command_args(args, min: 1, prefix: 'python')
240
+ args.join(' ')
221
241
  end
222
242
  shell(cmd, name: :exec, chdir: path)
223
243
  end
@@ -251,8 +271,8 @@ module Squared
251
271
  args = args.to_a
252
272
  if args.empty?
253
273
  args = readline('Enter command', force: true).split(' ', 2)
254
- elsif args.size == 1 && !option('interactive', equals: '0', prefix: ref)
255
- args << readline('Enter arguments', force: false) unless args.first.include?(' ')
274
+ elsif args.size == 1 && option('interactive', notequals: '0', prefix: ref)
275
+ args << readline('Enter arguments', force: false)
256
276
  end
257
277
  venv_init
258
278
  run args.join(' ')
@@ -274,9 +294,9 @@ module Squared
274
294
  when :freeze
275
295
  format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
276
296
  task flag do |_, args|
277
- if (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
278
- puts File.read(file)
279
- end
297
+ next unless (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
298
+
299
+ puts File.read(file)
280
300
  end
281
301
  when :uninstall
282
302
  format_desc action, flag, 'package+,opts*'
@@ -300,15 +320,12 @@ module Squared
300
320
  end
301
321
  when 'install'
302
322
  format_desc(action, flag, 'opts*', before: case flag
303
- when :target then 'dir'
304
- when :editable then 'path/url?,opts*'
305
- when :upgrade then 'strategy?,opts*,package+'
323
+ when :editable then 'path/url?'
324
+ when :upgrade then 'strategy?,package+'
325
+ when :requirement, :target then 'path'
326
+ else 'package+'
306
327
  end)
307
328
  case flag
308
- when :editable
309
- task flag do |_, args|
310
- install flag, args.to_a
311
- end
312
329
  when :upgrade
313
330
  task flag, [:strategy] do |_, args|
314
331
  install flag, (case args.strategy
@@ -324,14 +341,16 @@ module Squared
324
341
  end
325
342
  end)
326
343
  end
327
- when :target
328
- task flag, [:dir] do |_, args|
329
- dir = param_guard(action, flag, args: args, key: :dir)
330
- depend(flag, args.extras, target: dir)
344
+ when :requirement, :target
345
+ task flag, [:path] do |_, args|
346
+ path = param_guard(action, flag, args: args, key: :path)
347
+ depend(flag, args.extras, target: path)
331
348
  end
332
349
  else
350
+ next if flag == :poetry && !poetry?
351
+
333
352
  task flag do |_, args|
334
- depend flag, args.to_a
353
+ install flag, args.to_a
335
354
  end
336
355
  end
337
356
  when 'outdated'
@@ -389,22 +408,24 @@ module Squared
389
408
  cmd << '--no-root' if option('no-root')
390
409
  else
391
410
  cmd = pip_session 'install'
392
- cmd << '--upgrade-strategy=eager' if env('PYTHON_UPDATE')
411
+ cmd << '--upgrade-strategy=eager' if env('UPDATE') || env('PYTHON_UPDATE')
412
+ cmd << '--no-build-isolation' if option('build-isolation', equals: '0')
393
413
  if flag
394
- case flag
395
- when :user
396
- cmd << '--user'
397
- when :target
398
- cmd << quote_option('target', basepath(target))
399
- when :force
400
- cmd << '--force-reinstall'
401
- end
402
- op = append_pip(flag, opts, from: :install)
414
+ cmd << case flag
415
+ when :requirement, :target
416
+ quote_option(flag, basepath(target))
417
+ else
418
+ shell_option(flag)
419
+ end
420
+ op = append_pip flag, opts, pipopts(:install)
403
421
  op.clear
404
422
  else
405
423
  append_global
406
424
  end
407
- cmd << "-r #{DEP_PYTHON[4]}" if exist?(DEP_PYTHON[4]) && !session_arg?('r', 'requirement')
425
+ if exist?(DEP_PYTHON[4]) && !session_arg?('r', 'requirement')
426
+ cmd << basic_option('r', DEP_PYTHON[4])
427
+ cmd << basic_option('c', 'constraints.txt') if exist?('constraints.txt')
428
+ end
408
429
  append_editable
409
430
  end
410
431
  run(sync: sync, from: :depend)
@@ -436,7 +457,7 @@ module Squared
436
457
  :patch
437
458
  end
438
459
  end
439
- '--not-required' unless option('not-required', equals: '0')
460
+ '--not-required' if option('not-required', notequals: '0') && !env('PYTHON_DEFAULT')
440
461
  end
441
462
  cmd << '--local' if option('l', 'local')
442
463
  append_global
@@ -446,7 +467,7 @@ module Squared
446
467
  on :first, :outdated
447
468
  banner = format_banner cmd
448
469
  print_item banner if sync
449
- pwd_set(from: :outdated) do
470
+ pwd_set(cmd, dryrun: dryrun) do
450
471
  tc = theme[:current]
451
472
  start = 0
452
473
  found = 0
@@ -460,7 +481,8 @@ module Squared
460
481
  (venv ? command(runenv, cmd) : `#{cmd}`).lines(chomp: true)
461
482
  else
462
483
  IO.popen(runenv || {}, cmd).readlines(chomp: true)
463
- end.each do |line|
484
+ end
485
+ .each do |line|
464
486
  next if line.match?(/^[ -]+$/)
465
487
 
466
488
  if start > 0
@@ -523,7 +545,7 @@ module Squared
523
545
  if found > 0
524
546
  items = if se
525
547
  choice('Select a package', items.map(&:first),
526
- multiple: true, force: false, index: true, border: true).map! { |n| items[n.pred].last }
548
+ multiple: true, force: false, index: true, border: true).map { |n| items[n.pred].last }
527
549
  elsif ia
528
550
  items.map(&:last)
529
551
  else
@@ -539,10 +561,9 @@ module Squared
539
561
  if up && !items.empty?
540
562
  base = %w[eager no-deps]
541
563
  base << 'user' unless venv
542
- opts = (base & opts).map! { |val| val == 'eager' ? "upgrade-strategy=#{val}" : val }
564
+ opts = (base & opts).map { |val| val == 'eager' ? "upgrade-strategy=#{val}" : val }
543
565
  if dryrun
544
- opts.map! { |val| fill_option(val) }
545
- print_run pip_output('install --upgrade', *opts, *items.quote!), false
566
+ print_run pip_output('install --upgrade', *opts.map { |val| fill_option(val) }, *items.quote!), false
546
567
  else
547
568
  install(:upgrade, opts, packages: items, banner: false)
548
569
  end
@@ -555,8 +576,13 @@ module Squared
555
576
  on :last, :outdated
556
577
  end
557
578
 
558
- def install(flag, opts = [], packages: [], banner: true)
559
- op = append_pip(flag, opts, target: pip_session('install'), from: :install)
579
+ def install(flag, opts = [], packages: [], banner: !silent?)
580
+ if flag == :poetry
581
+ OptionPartition.new(opts, OPT_POETRY[:install] + OPT_POETRY[:common], poetry_session('install'),
582
+ project: self, single: singleopt(flag))
583
+ else
584
+ op = append_pip(flag, opts, pipopts(:install), target: pip_session('install'))
585
+ end
560
586
  case flag
561
587
  when :editable
562
588
  op << quote_option('e', op.pop || editable || '.')
@@ -571,7 +597,7 @@ module Squared
571
597
  run(banner: banner, from: :install)
572
598
  end
573
599
 
574
- def build!(flag, opts = [])
600
+ def build!(flag, opts = [], outdir: nil, srcdir: nil)
575
601
  list = case flag
576
602
  when :poetry
577
603
  cmd = poetry_session 'build'
@@ -582,6 +608,9 @@ module Squared
582
608
  when :hatch
583
609
  cmd, opts = hatch_session('build', opts: opts)
584
610
  OPT_HATCH[:build]
611
+ when :meson
612
+ cmd = session 'meson', 'setup'
613
+ OPT_MESON[:build]
585
614
  else
586
615
  cmd, opts = python_session('-m build', opts: opts)
587
616
  OPT_PYTHON[:build]
@@ -589,26 +618,45 @@ module Squared
589
618
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
590
619
  case flag
591
620
  when :hatch
592
- if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
621
+ op.add_path(outdir) if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
622
+ srcdir = false
623
+ when :meson
624
+ if outdir
593
625
  op.add_path(outdir)
626
+ else
627
+ outdir = op.first
628
+ op.add_first(path: true, expect: 'outdir not specified')
594
629
  end
595
630
  else
596
- unless op.empty?
597
- args = case flag
598
- when :poetry
599
- %w[o output]
600
- when :pdm
601
- %w[d dest]
602
- else
603
- srcdir = true
604
- %w[o outdir]
605
- end
606
- op << quote_option(args.last, basepath(op.shift)) unless op.arg?(*args)
631
+ args = case flag
632
+ when :poetry
633
+ srcdir = false
634
+ %w[o output]
635
+ when :pdm
636
+ srcdir = false
637
+ %w[d dest]
638
+ else
639
+ %w[o outdir]
640
+ end
641
+ outdir = op.shift unless op.arg?(*args)
642
+ op << quote_option(args.last, basepath(outdir)) if outdir
643
+ end
644
+ unless srcdir == false
645
+ if srcdir
646
+ op.add_path(srcdir)
647
+ else
648
+ op.exist?(add: true, first: true)
607
649
  end
608
650
  end
609
- op.exist?(add: true, first: true) if srcdir
610
- op.clear
611
- run(from: :"#{flag}:build")
651
+ from = symjoin flag, 'build'
652
+ if flag == :meson
653
+ cmd = session_output 'meson', 'compile', quote_option('C', basepath(outdir))
654
+ OptionPartition.new(op.extras, OPT_MESON[:compile], cmd, project: self).append unless op.empty?
655
+ run_s(op, cmd.done, from: from)
656
+ else
657
+ op.clear
658
+ run(from: from)
659
+ end
612
660
  end
613
661
 
614
662
  def publish(flag, opts = [], test: false)
@@ -655,12 +703,12 @@ module Squared
655
703
  op.add_path
656
704
  end
657
705
  end
658
- run(from: :"#{flag}:publish", interactive: ['Publish', 'N', project])
706
+ run(from: symjoin(flag, 'publish'), interactive: ['Publish', 'N', project])
659
707
  end
660
708
 
661
709
  def python(*args, sync: true, banner: verbose?, with: nil, pass: PASS_PYTHON[:python], **kwargs)
662
710
  op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_PYTHON[:common],
663
- session('python', path: venv.nil?),
711
+ session('python', path: !venv?),
664
712
  project: self, multiple: [/^-c/], single: singleopt(:python), args: true,
665
713
  stdin: true)
666
714
  op.concat(args)
@@ -676,7 +724,7 @@ module Squared
676
724
  end
677
725
  end
678
726
  print_run(op, banner, **kwargs)
679
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :python)
727
+ run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: :python)
680
728
  end
681
729
 
682
730
  def pip(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -687,7 +735,7 @@ module Squared
687
735
  when :freeze, :inspect, :list, :check, :completion, :debug
688
736
  opts.concat(args)
689
737
  end
690
- op = append_pip(flag, opts, target: pip_session(flag), from: flag)
738
+ op = append_pip(flag, opts, pipopts(flag), target: pip_session(flag))
691
739
  case flag
692
740
  when :install, :uninstall, :show, :index
693
741
  op.concat(args)
@@ -752,14 +800,24 @@ module Squared
752
800
  op.clear
753
801
  end
754
802
  print_run(op, banner, **kwargs)
755
- run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception), from: :"pip:#{flag}")
803
+ run(sync: sync, banner: banner, exception: kwargs.fetch(:exception, exception?), from: symjoin('pip', flag))
756
804
  .yield_self { |val| ret || val }
757
805
  end
758
806
 
807
+ def project
808
+ return @project unless @project.frozen?
809
+
810
+ @project = (read_pyproject('project', 'name') || @project).dup
811
+ end
812
+
813
+ def version
814
+ @version ||= read_pyproject('project', 'version')
815
+ end
816
+
759
817
  def variable_set(key, *args, **, &blk)
760
818
  if block_given?
761
819
  case key
762
- when :dependfile, :venv, :editable
820
+ when :dependfile, :editable, :venv
763
821
  args = block_args args, &blk
764
822
  end
765
823
  end
@@ -773,7 +831,7 @@ module Squared
773
831
  @dependindex = index
774
832
  @dependfile = val
775
833
  else
776
- log.warn "variable_set: @#{key}=#{val} (not supported)"
834
+ log.warn "variable_set: @dependfile=#{val}".subhint('not supported')
777
835
  end
778
836
  end
779
837
  when :editable
@@ -793,60 +851,51 @@ module Squared
793
851
  dependtype > 0 && !task_pass?('outdated')
794
852
  end
795
853
 
854
+ def venv?
855
+ !venv.nil?
856
+ end
857
+
796
858
  private
797
859
 
798
860
  def pip_session(*cmd)
799
- session('pip', *cmd, *preopts, path: venv.nil?)
861
+ session('pip', *cmd, *preopts, path: !venv?)
800
862
  end
801
863
 
802
864
  def pip_output(*cmd)
803
- session_output('pip', *cmd, *preopts, path: venv.nil?)
865
+ session_output('pip', *cmd, *preopts, path: !venv?)
804
866
  end
805
867
 
806
868
  def python_session(*cmd, opts: nil)
807
- pre = preopts(quiet: false)
808
- return session('python', *pre, *cmd, path: venv.nil?) unless opts
809
-
810
- op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
811
- [session('python', *pre, *op.to_a, *cmd, path: venv.nil?), op.extras]
812
- end
813
-
814
- def poetry_session(*cmd)
815
- ret = session('poetry', *cmd, *preopts)
816
- option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
817
- ret
869
+ create_session('python', *cmd, common: OPT_PYTHON[:common], opts: opts, quiet: false)
818
870
  end
819
871
 
820
872
  def pdm_session(*cmd, opts: nil)
821
- create_session(*cmd, name: 'pdm', common: OPT_PDM[:common], opts: opts)
873
+ create_session('pdm', *cmd, common: OPT_PDM[:common], opts: opts)
822
874
  end
823
875
 
824
876
  def hatch_session(*cmd, opts: nil)
825
- create_session(*cmd, name: 'hatch', common: OPT_HATCH[:common], opts: opts)
877
+ if (target = append_nocolor('no-color', target: [], prefix: 'hatch'))
878
+ (opts ||= []).concat(target)
879
+ end
880
+ create_session('hatch', *cmd, common: OPT_HATCH[:common], opts: opts)
826
881
  end
827
882
 
828
- def create_session(*cmd, name:, common:, opts: nil)
829
- return session(name, *preopts, *cmd, path: venv.nil?) unless opts
883
+ def poetry_session(*cmd)
884
+ ret = session('poetry', *cmd, *preopts)
885
+ option('project', ignore: false, path: true)
886
+ append_nocolor '--no-ansi'
887
+ ret
888
+ end
889
+
890
+ def create_session(name, *cmd, common:, opts: nil, quiet: true)
891
+ pre = preopts(quiet: quiet)
892
+ return session(name, *pre, *cmd, path: !venv?) unless opts
830
893
 
831
894
  op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
832
- [session(name, *preopts, *op.to_a, *cmd, path: venv.nil?), op.extras]
895
+ [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
833
896
  end
834
897
 
835
- def append_pip(flag, opts, target: @session, from: nil)
836
- list = OPT_PIP.fetch(from, []) + OPT_PIP[:common]
837
- if pip_install?(flag) || from == :install
838
- list.concat(OPT_PIP[:install_a])
839
- list.concat(OPT_PIP[:install_b]) unless flag == :index
840
- case flag
841
- when :install, :editable, :upgrade
842
- list.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
843
- when :lock, :wheel
844
- list.concat(OPT_PIP[:install_c])
845
- when :download, :index
846
- list.concat(OPT_PIP[:debug])
847
- end
848
- opts << 'no-build-isolation' if option('build-isolation', equals: '0')
849
- end
898
+ def append_pip(flag, opts, list, target: @session)
850
899
  op = OptionPartition.new(opts, list, target, project: self, single: singleopt)
851
900
  append_global(target: target)
852
901
  case flag
@@ -867,7 +916,7 @@ module Squared
867
916
  end
868
917
  op.swap
869
918
  if edit
870
- edit = basepath(edit) unless %r{\A[a-z]+(?:\+[a-z]+)?://}i.match?(edit)
919
+ edit = basepath(edit) unless %r{\A[a-z]+(\+[a-z]+)?://}i.match?(edit)
871
920
  if flag == :editable
872
921
  op.push(edit)
873
922
  else
@@ -898,18 +947,16 @@ module Squared
898
947
  end
899
948
 
900
949
  def append_global(target: @session)
901
- target.merge([
902
- option('cache-dir', target: target) do |val|
903
- case val
904
- when '0', 'false'
905
- '--no-cache-dir'
906
- else
907
- quote_option 'cache-dir', basepath(val)
908
- end
909
- end,
910
- option('proxy', target: target) { |val| quote_option('proxy', val) },
911
- option('python', target: target) { |val| quote_option('python', basepath(val)) }
912
- ])
950
+ option('cache-dir', target: target) do |val|
951
+ target << case val
952
+ when '0', 'false'
953
+ '--no-cache-dir'
954
+ else
955
+ quote_option('cache-dir', basepath(val))
956
+ end
957
+ end
958
+ option('proxy', target: target, quote: true)
959
+ option('python', target: target, path: true)
913
960
  append_nocolor(target: target)
914
961
  end
915
962
 
@@ -920,14 +967,18 @@ module Squared
920
967
  def read_pyproject(table, key = nil)
921
968
  return [] unless (file = pyprojectfile)
922
969
 
923
- unless (ret = (@pyproject ||= {})[table])
970
+ ret = (@pyproject ||= {})[table]
971
+ unless ret
924
972
  ret = []
973
+ found = false
925
974
  start = /^\s*\[#{Regexp.escape(table)}\]\s*$/
926
975
  ch = nil
927
- found = false
928
976
  File.foreach(file) do |line|
977
+ next if line.start_with?(/\s*#/)
978
+
929
979
  if found
930
- break if line.match?(/^\s*\[[\w.-]+\]\s*$/)
980
+ line.chomp!($1) if line =~ /(?<=[\d"'{}\[\]]|true|false)(\s*#.*)$/
981
+ break if line.match?(/^\s*\[(?:[\w.\-" ]+|".+"|'.+')\]\s*$/)
931
982
 
932
983
  if ch
933
984
  val = line.rstrip
@@ -943,9 +994,9 @@ module Squared
943
994
  end
944
995
  end
945
996
  ret.last[1] += val
946
- elsif (data = line.match(/^\s*(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*?))\s*$/))
947
- if (val = data[4])
948
- case (ch = data[3])
997
+ elsif line.strip =~ /^(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*))$/
998
+ if (val = $4)
999
+ case (ch = $3)
949
1000
  when '{', '['
950
1001
  val = "#{ch}#{val}"
951
1002
  ch = ch == '{' ? '}' : ']'
@@ -958,7 +1009,7 @@ module Squared
958
1009
  end
959
1010
  end
960
1011
  else
961
- val = case (ch = data[2])
1012
+ val = case (ch = $2)
962
1013
  when 'true'
963
1014
  true
964
1015
  when 'false'
@@ -967,7 +1018,10 @@ module Squared
967
1018
  ch.include?('.') ? ch.to_f : ch.to_i
968
1019
  end
969
1020
  end
970
- ret << [data[1], val]
1021
+ ret << [$1, val]
1022
+ end
1023
+ if !ch && (val = ret.last[1]).is_a?(String) && (val.match?(/\A\{.+\}\z/m) || val.match?(/\A\[.+\]\z/m))
1024
+ ret.last[1] = JSON.parse(val) rescue nil
971
1025
  end
972
1026
  else
973
1027
  found = line.match?(start)
@@ -975,9 +1029,7 @@ module Squared
975
1029
  end
976
1030
  @pyproject[table] = ret
977
1031
  end
978
- return ret.find { |val| val.first == key }&.last if key
979
-
980
- ret
1032
+ key ? ret.find { |val| val.first == key }&.last : ret
981
1033
  end
982
1034
 
983
1035
  def pyprojectfile
@@ -989,9 +1041,9 @@ module Squared
989
1041
  case flag
990
1042
  when :python
991
1043
  /\A(?:v+|q+|b+|V+|O+)\z/
992
- when :pdm
1044
+ when :pdm, :poetry
993
1045
  /\Av+\z/
994
- when :twine
1046
+ when :twine, :meson
995
1047
  nil
996
1048
  else
997
1049
  /\A(?:v+|q+)\z/
@@ -1001,7 +1053,7 @@ module Squared
1001
1053
  def preopts(quiet: true)
1002
1054
  ret = []
1003
1055
  case verbose
1004
- when FalseClass
1056
+ when false
1005
1057
  ret << '--quiet' if quiet
1006
1058
  when Numeric
1007
1059
  ret << "-#{'v' * verbose}" if verbose > 0
@@ -1009,6 +1061,23 @@ module Squared
1009
1061
  ret
1010
1062
  end
1011
1063
 
1064
+ def pipopts(flag)
1065
+ ret = OPT_PIP.fetch(flag, []) + OPT_PIP[:common]
1066
+ if pip_install?(flag)
1067
+ ret.concat(OPT_PIP[:install_a])
1068
+ ret.concat(OPT_PIP[:install_b]) unless flag == :index
1069
+ case flag
1070
+ when :install
1071
+ ret.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
1072
+ when :lock, :wheel
1073
+ ret.concat(OPT_PIP[:install_c])
1074
+ when :download, :index
1075
+ ret.concat(OPT_PIP[:debug])
1076
+ end
1077
+ end
1078
+ ret
1079
+ end
1080
+
1012
1081
  def variables
1013
1082
  (super + %i[venv editable]).freeze
1014
1083
  end
@@ -1037,11 +1106,16 @@ module Squared
1037
1106
  end
1038
1107
 
1039
1108
  def venv_set(val)
1109
+ @venv = nil
1040
1110
  return unless val
1041
1111
 
1042
- write = ->(level, hint) { log.add(level, "venv: #{@venv}".subhint(hint)) }
1112
+ write = ->(level, hint) { log.add(level, "#{virtualenv? ? 'virtualenv' : 'venv'}: #{@venv}".subhint(hint)) }
1043
1113
  if val.is_a?(Array)
1044
1114
  val, *opts = val
1115
+ if opts.first == 'virtualenv'
1116
+ @virtualenv = true
1117
+ opts.shift
1118
+ end
1045
1119
  @venvopts = opts
1046
1120
  end
1047
1121
  @venv = basepath val
@@ -1061,21 +1135,35 @@ module Squared
1061
1135
  def venv_init
1062
1136
  return if !venv || (venvbin.directory? && !venvbin.empty?)
1063
1137
 
1064
- puts log_message(venv, subject: 'venv', hint: 'init') unless silent?
1065
- opts = @venvopts&.map { |val| OptionPartition.strip(val) }&.flatten
1066
- venv_create(venv, opts || ["prompt=#{name}", 'upgrade-deps'], env: false, banner: false)
1067
- puts log_message(venv, subject: 'venv', hint: 'created') unless silent?
1138
+ subject = virtualenv? ? 'virtualenv' : 'venv'
1139
+ puts log_message(venv, subject: subject, hint: 'init') unless silent?
1140
+ opts = @venvopts ? @venvopts.map { |val| OptionPartition.strip(val) }.flatten : [basic_option('prompt', name)]
1141
+ venv_create(venv, opts, env: false, banner: false)
1142
+ puts log_message(venv, subject: subject, hint: 'created') unless silent?
1068
1143
  end
1069
1144
 
1070
1145
  def venv_create(dir, opts = [], env: nil, banner: banner?)
1071
- cmd, opts = python_session('-m venv', opts: opts)
1072
- op = OptionPartition.new(opts, OPT_PYTHON[:venv], cmd, project: self)
1073
- status = op.append(dir, delim: true)
1074
- .clear(pass: false)
1075
- .arg?(/\A-v+\z/)
1146
+ flag = if virtualenv?
1147
+ status = true
1148
+ :virtualenv
1149
+ else
1150
+ :venv
1151
+ end
1152
+ cmd, opts = python_session('-m', flag, opts: opts)
1153
+ op = OptionPartition.new(opts, OPT_PYTHON[flag], cmd, project: self)
1154
+ op.append(dir, delim: true)
1155
+ .clear(pass: false)
1156
+ status ||= op.arg?(/\A-v+\z/)
1076
1157
  ret = run(op, env, exception: true, banner: banner)
1077
- pip(:install, 'poetry', banner: false) if poetry?
1078
- success?(ret, banner, !status) { |out| puts(out && dir.directory? ? "Success: #{dir}" : 'Failed') }
1158
+ args = []
1159
+ requires = read_pyproject 'build-system', 'requires'
1160
+ args.concat(requires) if requires.is_a?(Array)
1161
+ if poetry?
1162
+ requires = read_pyproject 'tool.poetry', 'requires-poetry'
1163
+ args << "poetry#{requires}"
1164
+ end
1165
+ pip(:install, *args, banner: false) unless args.empty?
1166
+ success?(ret, banner, !status) { |ret| puts(ret && dir.directory? ? "Success: #{dir}" : 'Failed') }
1079
1167
  end
1080
1168
 
1081
1169
  def pip_install?(flag)
@@ -1092,6 +1180,8 @@ module Squared
1092
1180
  build_backend == 'hatchling.build'
1093
1181
  when :setuptools
1094
1182
  build_backend == 'setuptools.build_meta'
1183
+ when :meson
1184
+ build_backend != 'mesonpy' && exist?('meson.build')
1095
1185
  end
1096
1186
  end
1097
1187
 
@@ -1107,6 +1197,10 @@ module Squared
1107
1197
  dependtype == 1
1108
1198
  end
1109
1199
 
1200
+ def virtualenv?
1201
+ @virtualenv ||= exist?('virtualenv.ini') || !ENV['VIRTUALENV_PYTHON'].nil?
1202
+ end
1203
+
1110
1204
  def requirements?
1111
1205
  dependtype == 5
1112
1206
  end