squared 0.6.13 → 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,19 +117,18 @@ 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)
115
130
  end
116
- dependfile_set(DEP_PYTHON, default: 2)
131
+ dependfile_set DEP_PYTHON
117
132
  editable_set editable
118
133
  venv_set kwargs[:venv]
119
134
  end
@@ -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/
@@ -204,12 +223,12 @@ module Squared
204
223
  end
205
224
  next if found.anybits?(1)
206
225
 
207
- puts log_message(found == 0 ? Logger::INFO : Logger::WARN,
226
+ puts log_message(found == 0 ? Logger::INFO : Logger.WARN,
208
227
  "no scripts #{found == 0 ? 'found' : 'executed'}",
209
228
  subject: name, hint: pyprojectfile)
210
229
  end
211
230
  when 'exec'
212
- format_desc action, nil, 'command|:,args*'
231
+ format_desc action, nil, ':|command,args*'
213
232
  task action do |_, args|
214
233
  args = args.to_a
215
234
  cmd = if (i = args.delete(':')) && !workspace.windows?
@@ -217,9 +236,10 @@ 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
- run(cmd, send: :exec, banner: false)
242
+ shell(cmd, name: :exec, chdir: path)
223
243
  end
224
244
  end
225
245
  else
@@ -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)
@@ -710,9 +758,7 @@ module Squared
710
758
  when :freeze
711
759
  venv_init
712
760
  op << '>'
713
- file = op.detect { |val| op.exist?(val) }
714
- op.remove(file) if file
715
- op.add_quote(ret = basepath(file || DEP_PYTHON[4]))
761
+ op.add_quote(ret = basepath(op.detect { |val| op.exist?(val) } || DEP_PYTHON[4]))
716
762
  .clear
717
763
  when :cache
718
764
  op.concat(args)
@@ -754,14 +800,24 @@ module Squared
754
800
  op.clear
755
801
  end
756
802
  print_run(op, banner, **kwargs)
757
- 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))
758
804
  .yield_self { |val| ret || val }
759
805
  end
760
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
+
761
817
  def variable_set(key, *args, **, &blk)
762
818
  if block_given?
763
819
  case key
764
- when :dependfile, :venv, :editable
820
+ when :dependfile, :editable, :venv
765
821
  args = block_args args, &blk
766
822
  end
767
823
  end
@@ -775,7 +831,7 @@ module Squared
775
831
  @dependindex = index
776
832
  @dependfile = val
777
833
  else
778
- log.warn "variable_set: @#{key}=#{val} (not supported)"
834
+ log.warn "variable_set: @dependfile=#{val}".subhint('not supported')
779
835
  end
780
836
  end
781
837
  when :editable
@@ -795,60 +851,51 @@ module Squared
795
851
  dependtype > 0 && !task_pass?('outdated')
796
852
  end
797
853
 
854
+ def venv?
855
+ !venv.nil?
856
+ end
857
+
798
858
  private
799
859
 
800
860
  def pip_session(*cmd)
801
- session('pip', *cmd, *preopts, path: venv.nil?)
861
+ session('pip', *cmd, *preopts, path: !venv?)
802
862
  end
803
863
 
804
864
  def pip_output(*cmd)
805
- session_output('pip', *cmd, *preopts, path: venv.nil?)
865
+ session_output('pip', *cmd, *preopts, path: !venv?)
806
866
  end
807
867
 
808
868
  def python_session(*cmd, opts: nil)
809
- pre = preopts(quiet: false)
810
- return session('python', *pre, *cmd, path: venv.nil?) unless opts
811
-
812
- op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
813
- [session('python', *pre, *op.to_a, *cmd, path: venv.nil?), op.extras]
814
- end
815
-
816
- def poetry_session(*cmd)
817
- ret = session('poetry', *cmd, *preopts)
818
- option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
819
- ret
869
+ create_session('python', *cmd, common: OPT_PYTHON[:common], opts: opts, quiet: false)
820
870
  end
821
871
 
822
872
  def pdm_session(*cmd, opts: nil)
823
- create_session(*cmd, name: 'pdm', common: OPT_PDM[:common], opts: opts)
873
+ create_session('pdm', *cmd, common: OPT_PDM[:common], opts: opts)
824
874
  end
825
875
 
826
876
  def hatch_session(*cmd, opts: nil)
827
- 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)
828
881
  end
829
882
 
830
- def create_session(*cmd, name:, common:, opts: nil)
831
- 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
832
893
 
833
894
  op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
834
- [session(name, *preopts, *op.to_a, *cmd, path: venv.nil?), op.extras]
895
+ [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
835
896
  end
836
897
 
837
- def append_pip(flag, opts, target: @session, from: nil)
838
- list = OPT_PIP.fetch(from, []) + OPT_PIP[:common]
839
- if pip_install?(flag) || from == :install
840
- list.concat(OPT_PIP[:install_a])
841
- list.concat(OPT_PIP[:install_b]) unless flag == :index
842
- case flag
843
- when :install, :editable, :upgrade
844
- list.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
845
- when :lock, :wheel
846
- list.concat(OPT_PIP[:install_c])
847
- when :download, :index
848
- list.concat(OPT_PIP[:debug])
849
- end
850
- opts << 'no-build-isolation' if option('build-isolation', equals: '0')
851
- end
898
+ def append_pip(flag, opts, list, target: @session)
852
899
  op = OptionPartition.new(opts, list, target, project: self, single: singleopt)
853
900
  append_global(target: target)
854
901
  case flag
@@ -869,7 +916,7 @@ module Squared
869
916
  end
870
917
  op.swap
871
918
  if edit
872
- 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)
873
920
  if flag == :editable
874
921
  op.push(edit)
875
922
  else
@@ -900,18 +947,16 @@ module Squared
900
947
  end
901
948
 
902
949
  def append_global(target: @session)
903
- target.merge([
904
- option('cache-dir', target: target) do |val|
905
- case val
906
- when '0', 'false'
907
- '--no-cache-dir'
908
- else
909
- quote_option 'cache-dir', basepath(val)
910
- end
911
- end,
912
- option('proxy', target: target) { |val| quote_option('proxy', val) },
913
- option('python', target: target) { |val| quote_option('python', basepath(val)) }
914
- ])
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)
915
960
  append_nocolor(target: target)
916
961
  end
917
962
 
@@ -922,14 +967,18 @@ module Squared
922
967
  def read_pyproject(table, key = nil)
923
968
  return [] unless (file = pyprojectfile)
924
969
 
925
- unless (ret = (@pyproject ||= {})[table])
970
+ ret = (@pyproject ||= {})[table]
971
+ unless ret
926
972
  ret = []
973
+ found = false
927
974
  start = /^\s*\[#{Regexp.escape(table)}\]\s*$/
928
975
  ch = nil
929
- found = false
930
976
  File.foreach(file) do |line|
977
+ next if line.start_with?(/\s*#/)
978
+
931
979
  if found
932
- 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*$/)
933
982
 
934
983
  if ch
935
984
  val = line.rstrip
@@ -945,9 +994,9 @@ module Squared
945
994
  end
946
995
  end
947
996
  ret.last[1] += val
948
- elsif (data = line.match(/^\s*(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*?))\s*$/))
949
- if (val = data[4])
950
- case (ch = data[3])
997
+ elsif line.strip =~ /^(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*))$/
998
+ if (val = $4)
999
+ case (ch = $3)
951
1000
  when '{', '['
952
1001
  val = "#{ch}#{val}"
953
1002
  ch = ch == '{' ? '}' : ']'
@@ -960,7 +1009,7 @@ module Squared
960
1009
  end
961
1010
  end
962
1011
  else
963
- val = case (ch = data[2])
1012
+ val = case (ch = $2)
964
1013
  when 'true'
965
1014
  true
966
1015
  when 'false'
@@ -969,7 +1018,10 @@ module Squared
969
1018
  ch.include?('.') ? ch.to_f : ch.to_i
970
1019
  end
971
1020
  end
972
- 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
973
1025
  end
974
1026
  else
975
1027
  found = line.match?(start)
@@ -977,9 +1029,7 @@ module Squared
977
1029
  end
978
1030
  @pyproject[table] = ret
979
1031
  end
980
- return ret.find { |val| val.first == key }&.last if key
981
-
982
- ret
1032
+ key ? ret.find { |val| val.first == key }&.last : ret
983
1033
  end
984
1034
 
985
1035
  def pyprojectfile
@@ -991,9 +1041,9 @@ module Squared
991
1041
  case flag
992
1042
  when :python
993
1043
  /\A(?:v+|q+|b+|V+|O+)\z/
994
- when :pdm
1044
+ when :pdm, :poetry
995
1045
  /\Av+\z/
996
- when :twine
1046
+ when :twine, :meson
997
1047
  nil
998
1048
  else
999
1049
  /\A(?:v+|q+)\z/
@@ -1003,7 +1053,7 @@ module Squared
1003
1053
  def preopts(quiet: true)
1004
1054
  ret = []
1005
1055
  case verbose
1006
- when FalseClass
1056
+ when false
1007
1057
  ret << '--quiet' if quiet
1008
1058
  when Numeric
1009
1059
  ret << "-#{'v' * verbose}" if verbose > 0
@@ -1011,6 +1061,23 @@ module Squared
1011
1061
  ret
1012
1062
  end
1013
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
+
1014
1081
  def variables
1015
1082
  (super + %i[venv editable]).freeze
1016
1083
  end
@@ -1039,11 +1106,16 @@ module Squared
1039
1106
  end
1040
1107
 
1041
1108
  def venv_set(val)
1109
+ @venv = nil
1042
1110
  return unless val
1043
1111
 
1044
- write = ->(level, hint) { log.add(level, "venv: #{@venv}".subhint(hint)) }
1112
+ write = ->(level, hint) { log.add(level, "#{virtualenv? ? 'virtualenv' : 'venv'}: #{@venv}".subhint(hint)) }
1045
1113
  if val.is_a?(Array)
1046
1114
  val, *opts = val
1115
+ if opts.first == 'virtualenv'
1116
+ @virtualenv = true
1117
+ opts.shift
1118
+ end
1047
1119
  @venvopts = opts
1048
1120
  end
1049
1121
  @venv = basepath val
@@ -1063,25 +1135,35 @@ module Squared
1063
1135
  def venv_init
1064
1136
  return if !venv || (venvbin.directory? && !venvbin.empty?)
1065
1137
 
1066
- puts log_message(venv, subject: 'venv', hint: 'init') unless silent?
1067
- opts = @venvopts&.map { |val| OptionPartition.strip(val) }&.flatten
1068
- venv_create(venv, opts || ["prompt=#{name}", 'upgrade-deps'], env: false, banner: false)
1069
- 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?
1070
1143
  end
1071
1144
 
1072
1145
  def venv_create(dir, opts = [], env: nil, banner: banner?)
1073
- cmd, opts = python_session('-m venv', opts: opts)
1074
- op = OptionPartition.new(opts, OPT_PYTHON[:venv], cmd, project: self)
1075
- status = op.append(dir, delim: true)
1076
- .clear(pass: false)
1077
- .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/)
1078
1157
  ret = run(op, env, exception: true, banner: banner)
1158
+ args = []
1159
+ requires = read_pyproject 'build-system', 'requires'
1160
+ args.concat(requires) if requires.is_a?(Array)
1079
1161
  if poetry?
1080
- pip(:install, 'poetry', banner: false)
1081
- elsif setuptools?
1082
- pip(:install, 'setuptools', 'wheel', banner: false)
1162
+ requires = read_pyproject 'tool.poetry', 'requires-poetry'
1163
+ args << "poetry#{requires}"
1083
1164
  end
1084
- success?(ret, banner, !status) { |out| puts(out && dir.directory? ? "Success: #{dir}" : 'Failed') }
1165
+ pip(:install, *args, banner: false) unless args.empty?
1166
+ success?(ret, banner, !status) { |ret| puts(ret && dir.directory? ? "Success: #{dir}" : 'Failed') }
1085
1167
  end
1086
1168
 
1087
1169
  def pip_install?(flag)
@@ -1098,6 +1180,8 @@ module Squared
1098
1180
  build_backend == 'hatchling.build'
1099
1181
  when :setuptools
1100
1182
  build_backend == 'setuptools.build_meta'
1183
+ when :meson
1184
+ build_backend != 'mesonpy' && exist?('meson.build')
1101
1185
  end
1102
1186
  end
1103
1187
 
@@ -1113,6 +1197,10 @@ module Squared
1113
1197
  dependtype == 1
1114
1198
  end
1115
1199
 
1200
+ def virtualenv?
1201
+ @virtualenv ||= exist?('virtualenv.ini') || !ENV['VIRTUALENV_PYTHON'].nil?
1202
+ end
1203
+
1116
1204
  def requirements?
1117
1205
  dependtype == 5
1118
1206
  end