squared 0.6.9 → 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
@@ -43,7 +48,9 @@ module Squared
43
48
  wheel: %w[no-verify w|wheel-dir=p].freeze
44
49
  }.freeze
45
50
  OPT_POETRY = {
46
- 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,
47
54
  build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
48
55
  publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=q r|repository=q
49
56
  u|username=qq].freeze
@@ -67,6 +74,17 @@ module Squared
67
74
  client-cert=p c|comment=q config-file=p i|identity=b p|password=q r|repository=b repository-url=q
68
75
  sign-with=b u|username=qq].freeze
69
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
70
88
  PASS_PYTHON = {
71
89
  python: %w[c v V].freeze,
72
90
  pip: {
@@ -85,7 +103,7 @@ module Squared
85
103
  end
86
104
 
87
105
  def bannerargs
88
- %i[dependfile venv].freeze
106
+ %i[version dependfile venv].freeze
89
107
  end
90
108
 
91
109
  def venv?
@@ -105,7 +123,7 @@ module Squared
105
123
  super
106
124
  if @pass.include?(Python.ref)
107
125
  initialize_ref Python.ref
108
- initialize_logger(**kwargs)
126
+ initialize_logger kwargs[:log]
109
127
  else
110
128
  initialize_build(Python.ref, **kwargs)
111
129
  initialize_env(**kwargs)
@@ -118,14 +136,18 @@ module Squared
118
136
  subtasks({
119
137
  'venv' => %i[exec create remove show].freeze,
120
138
  'pip' => %i[upgrade uninstall wheel reinstall freeze].freeze,
121
- 'install' => %i[user force upgrade target editable].freeze,
139
+ 'install' => %i[requirement target upgrade editable poetry].freeze,
122
140
  'outdated' => %i[major minor patch].freeze,
123
- 'build' => %i[poetry pdm hatch python].freeze,
141
+ 'build' => %i[poetry pdm hatch meson python].freeze,
124
142
  'publish' => %i[poetry pdm hatch twine].freeze,
125
143
  'run' => nil,
126
144
  'exec' => nil
127
145
  })
128
146
 
147
+ def project=(val)
148
+ @project = val.dup
149
+ end
150
+
129
151
  def verbose=(val)
130
152
  case val
131
153
  when /\Av+\z/
@@ -214,7 +236,8 @@ module Squared
214
236
  elsif i || args.empty?
215
237
  readline('Enter command', force: true)
216
238
  else
217
- (args << command_args(args, min: 1, prefix: 'python')).compact.join(' ')
239
+ command_args(args, min: 1, prefix: 'python')
240
+ args.join(' ')
218
241
  end
219
242
  shell(cmd, name: :exec, chdir: path)
220
243
  end
@@ -248,7 +271,7 @@ module Squared
248
271
  args = args.to_a
249
272
  if args.empty?
250
273
  args = readline('Enter command', force: true).split(' ', 2)
251
- elsif args.size == 1 && !option('interactive', equals: '0', prefix: ref)
274
+ elsif args.size == 1 && option('interactive', notequals: '0', prefix: ref)
252
275
  args << readline('Enter arguments', force: false)
253
276
  end
254
277
  venv_init
@@ -271,9 +294,9 @@ module Squared
271
294
  when :freeze
272
295
  format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
273
296
  task flag do |_, args|
274
- if (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
275
- puts File.read(file)
276
- end
297
+ next unless (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
298
+
299
+ puts File.read(file)
277
300
  end
278
301
  when :uninstall
279
302
  format_desc action, flag, 'package+,opts*'
@@ -297,15 +320,12 @@ module Squared
297
320
  end
298
321
  when 'install'
299
322
  format_desc(action, flag, 'opts*', before: case flag
300
- when :target then 'dir'
301
- when :editable then 'path/url?,opts*'
302
- 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+'
303
327
  end)
304
328
  case flag
305
- when :editable
306
- task flag do |_, args|
307
- install flag, args.to_a
308
- end
309
329
  when :upgrade
310
330
  task flag, [:strategy] do |_, args|
311
331
  install flag, (case args.strategy
@@ -321,14 +341,16 @@ module Squared
321
341
  end
322
342
  end)
323
343
  end
324
- when :target
325
- task flag, [:dir] do |_, args|
326
- dir = param_guard(action, flag, args: args, key: :dir)
327
- 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)
328
348
  end
329
349
  else
350
+ next if flag == :poetry && !poetry?
351
+
330
352
  task flag do |_, args|
331
- depend flag, args.to_a
353
+ install flag, args.to_a
332
354
  end
333
355
  end
334
356
  when 'outdated'
@@ -386,22 +408,24 @@ module Squared
386
408
  cmd << '--no-root' if option('no-root')
387
409
  else
388
410
  cmd = pip_session 'install'
389
- 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')
390
413
  if flag
391
- case flag
392
- when :user
393
- cmd << '--user'
394
- when :target
395
- cmd << quote_option('target', basepath(target))
396
- when :force
397
- cmd << '--force-reinstall'
398
- end
399
- 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)
400
421
  op.clear
401
422
  else
402
423
  append_global
403
424
  end
404
- 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
405
429
  append_editable
406
430
  end
407
431
  run(sync: sync, from: :depend)
@@ -433,7 +457,7 @@ module Squared
433
457
  :patch
434
458
  end
435
459
  end
436
- '--not-required' unless option('not-required', equals: '0')
460
+ '--not-required' if option('not-required', notequals: '0') && !env('PYTHON_DEFAULT')
437
461
  end
438
462
  cmd << '--local' if option('l', 'local')
439
463
  append_global
@@ -443,7 +467,7 @@ module Squared
443
467
  on :first, :outdated
444
468
  banner = format_banner cmd
445
469
  print_item banner if sync
446
- pwd_set(from: :outdated) do
470
+ pwd_set(cmd, dryrun: dryrun) do
447
471
  tc = theme[:current]
448
472
  start = 0
449
473
  found = 0
@@ -457,7 +481,8 @@ module Squared
457
481
  (venv ? command(runenv, cmd) : `#{cmd}`).lines(chomp: true)
458
482
  else
459
483
  IO.popen(runenv || {}, cmd).readlines(chomp: true)
460
- end.each do |line|
484
+ end
485
+ .each do |line|
461
486
  next if line.match?(/^[ -]+$/)
462
487
 
463
488
  if start > 0
@@ -520,7 +545,7 @@ module Squared
520
545
  if found > 0
521
546
  items = if se
522
547
  choice('Select a package', items.map(&:first),
523
- 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 }
524
549
  elsif ia
525
550
  items.map(&:last)
526
551
  else
@@ -536,10 +561,9 @@ module Squared
536
561
  if up && !items.empty?
537
562
  base = %w[eager no-deps]
538
563
  base << 'user' unless venv
539
- opts = (base & opts).map! { |val| val == 'eager' ? "upgrade-strategy=#{val}" : val }
564
+ opts = (base & opts).map { |val| val == 'eager' ? "upgrade-strategy=#{val}" : val }
540
565
  if dryrun
541
- opts.map! { |val| fill_option(val) }
542
- 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
543
567
  else
544
568
  install(:upgrade, opts, packages: items, banner: false)
545
569
  end
@@ -552,8 +576,13 @@ module Squared
552
576
  on :last, :outdated
553
577
  end
554
578
 
555
- def install(flag, opts = [], packages: [], banner: true)
556
- 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
557
586
  case flag
558
587
  when :editable
559
588
  op << quote_option('e', op.pop || editable || '.')
@@ -568,7 +597,7 @@ module Squared
568
597
  run(banner: banner, from: :install)
569
598
  end
570
599
 
571
- def build!(flag, opts = [])
600
+ def build!(flag, opts = [], outdir: nil, srcdir: nil)
572
601
  list = case flag
573
602
  when :poetry
574
603
  cmd = poetry_session 'build'
@@ -579,6 +608,9 @@ module Squared
579
608
  when :hatch
580
609
  cmd, opts = hatch_session('build', opts: opts)
581
610
  OPT_HATCH[:build]
611
+ when :meson
612
+ cmd = session 'meson', 'setup'
613
+ OPT_MESON[:build]
582
614
  else
583
615
  cmd, opts = python_session('-m build', opts: opts)
584
616
  OPT_PYTHON[:build]
@@ -586,26 +618,45 @@ module Squared
586
618
  op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
587
619
  case flag
588
620
  when :hatch
589
- 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
590
625
  op.add_path(outdir)
626
+ else
627
+ outdir = op.first
628
+ op.add_first(path: true, expect: 'outdir not specified')
591
629
  end
592
630
  else
593
- unless op.empty?
594
- args = case flag
595
- when :poetry
596
- %w[o output]
597
- when :pdm
598
- %w[d dest]
599
- else
600
- srcdir = true
601
- %w[o outdir]
602
- end
603
- 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)
604
649
  end
605
650
  end
606
- op.exist?(add: true, first: true) if srcdir
607
- op.clear
608
- 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
609
660
  end
610
661
 
611
662
  def publish(flag, opts = [], test: false)
@@ -652,12 +703,12 @@ module Squared
652
703
  op.add_path
653
704
  end
654
705
  end
655
- run(from: :"#{flag}:publish", interactive: ['Publish', 'N', project])
706
+ run(from: symjoin(flag, 'publish'), interactive: ['Publish', 'N', project])
656
707
  end
657
708
 
658
709
  def python(*args, sync: true, banner: verbose?, with: nil, pass: PASS_PYTHON[:python], **kwargs)
659
710
  op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_PYTHON[:common],
660
- session('python', path: venv.nil?),
711
+ session('python', path: !venv?),
661
712
  project: self, multiple: [/^-c/], single: singleopt(:python), args: true,
662
713
  stdin: true)
663
714
  op.concat(args)
@@ -673,7 +724,7 @@ module Squared
673
724
  end
674
725
  end
675
726
  print_run(op, banner, **kwargs)
676
- 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)
677
728
  end
678
729
 
679
730
  def pip(flag, *args, sync: true, banner: verbose?, with: nil, pass: nil, **kwargs)
@@ -684,7 +735,7 @@ module Squared
684
735
  when :freeze, :inspect, :list, :check, :completion, :debug
685
736
  opts.concat(args)
686
737
  end
687
- op = append_pip(flag, opts, target: pip_session(flag), from: flag)
738
+ op = append_pip(flag, opts, pipopts(flag), target: pip_session(flag))
688
739
  case flag
689
740
  when :install, :uninstall, :show, :index
690
741
  op.concat(args)
@@ -749,14 +800,24 @@ module Squared
749
800
  op.clear
750
801
  end
751
802
  print_run(op, banner, **kwargs)
752
- 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))
753
804
  .yield_self { |val| ret || val }
754
805
  end
755
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
+
756
817
  def variable_set(key, *args, **, &blk)
757
818
  if block_given?
758
819
  case key
759
- when :dependfile, :venv, :editable
820
+ when :dependfile, :editable, :venv
760
821
  args = block_args args, &blk
761
822
  end
762
823
  end
@@ -770,7 +831,7 @@ module Squared
770
831
  @dependindex = index
771
832
  @dependfile = val
772
833
  else
773
- log.warn "variable_set: @#{key}=#{val} (not supported)"
834
+ log.warn "variable_set: @dependfile=#{val}".subhint('not supported')
774
835
  end
775
836
  end
776
837
  when :editable
@@ -790,60 +851,51 @@ module Squared
790
851
  dependtype > 0 && !task_pass?('outdated')
791
852
  end
792
853
 
854
+ def venv?
855
+ !venv.nil?
856
+ end
857
+
793
858
  private
794
859
 
795
860
  def pip_session(*cmd)
796
- session('pip', *cmd, *preopts, path: venv.nil?)
861
+ session('pip', *cmd, *preopts, path: !venv?)
797
862
  end
798
863
 
799
864
  def pip_output(*cmd)
800
- session_output('pip', *cmd, *preopts, path: venv.nil?)
865
+ session_output('pip', *cmd, *preopts, path: !venv?)
801
866
  end
802
867
 
803
868
  def python_session(*cmd, opts: nil)
804
- pre = preopts(quiet: false)
805
- return session('python', *pre, *cmd, path: venv.nil?) unless opts
806
-
807
- op = OptionPartition.new(opts, OPT_PYTHON[:common], project: self, single: singleopt(:python))
808
- [session('python', *pre, *op.to_a, *cmd, path: venv.nil?), op.extras]
809
- end
810
-
811
- def poetry_session(*cmd)
812
- ret = session('poetry', *cmd, *preopts)
813
- option('project', ignore: false) { |val| ret << quote_option('project', basepath(val)) }
814
- ret
869
+ create_session('python', *cmd, common: OPT_PYTHON[:common], opts: opts, quiet: false)
815
870
  end
816
871
 
817
872
  def pdm_session(*cmd, opts: nil)
818
- create_session(*cmd, name: 'pdm', common: OPT_PDM[:common], opts: opts)
873
+ create_session('pdm', *cmd, common: OPT_PDM[:common], opts: opts)
819
874
  end
820
875
 
821
876
  def hatch_session(*cmd, opts: nil)
822
- 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)
823
881
  end
824
882
 
825
- def create_session(*cmd, name:, common:, opts: nil)
826
- 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
827
893
 
828
894
  op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
829
- [session(name, *preopts, *op.to_a, *cmd, path: venv.nil?), op.extras]
895
+ [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
830
896
  end
831
897
 
832
- def append_pip(flag, opts, target: @session, from: nil)
833
- list = OPT_PIP.fetch(from, []) + OPT_PIP[:common]
834
- if pip_install?(flag) || from == :install
835
- list.concat(OPT_PIP[:install_a])
836
- list.concat(OPT_PIP[:install_b]) unless flag == :index
837
- case flag
838
- when :install, :editable, :upgrade
839
- list.concat(OPT_PIP[:install_c] + OPT_PIP[:debug])
840
- when :lock, :wheel
841
- list.concat(OPT_PIP[:install_c])
842
- when :download, :index
843
- list.concat(OPT_PIP[:debug])
844
- end
845
- opts << 'no-build-isolation' if option('build-isolation', equals: '0')
846
- end
898
+ def append_pip(flag, opts, list, target: @session)
847
899
  op = OptionPartition.new(opts, list, target, project: self, single: singleopt)
848
900
  append_global(target: target)
849
901
  case flag
@@ -864,7 +916,7 @@ module Squared
864
916
  end
865
917
  op.swap
866
918
  if edit
867
- 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)
868
920
  if flag == :editable
869
921
  op.push(edit)
870
922
  else
@@ -895,18 +947,16 @@ module Squared
895
947
  end
896
948
 
897
949
  def append_global(target: @session)
898
- target.merge([
899
- option('cache-dir', target: target) do |val|
900
- case val
901
- when '0', 'false'
902
- '--no-cache-dir'
903
- else
904
- quote_option 'cache-dir', basepath(val)
905
- end
906
- end,
907
- option('proxy', target: target) { |val| quote_option('proxy', val) },
908
- option('python', target: target) { |val| quote_option('python', basepath(val)) }
909
- ])
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)
910
960
  append_nocolor(target: target)
911
961
  end
912
962
 
@@ -917,14 +967,18 @@ module Squared
917
967
  def read_pyproject(table, key = nil)
918
968
  return [] unless (file = pyprojectfile)
919
969
 
920
- unless (ret = (@pyproject ||= {})[table])
970
+ ret = (@pyproject ||= {})[table]
971
+ unless ret
921
972
  ret = []
973
+ found = false
922
974
  start = /^\s*\[#{Regexp.escape(table)}\]\s*$/
923
975
  ch = nil
924
- found = false
925
976
  File.foreach(file) do |line|
977
+ next if line.start_with?(/\s*#/)
978
+
926
979
  if found
927
- 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*$/)
928
982
 
929
983
  if ch
930
984
  val = line.rstrip
@@ -940,9 +994,9 @@ module Squared
940
994
  end
941
995
  end
942
996
  ret.last[1] += val
943
- elsif (data = line.match(/^\s*(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*?))\s*$/))
944
- if (val = data[4])
945
- case (ch = data[3])
997
+ elsif line.strip =~ /^(\S+)\s*=\s*([+-]?[\d.]+|true|false|("""|'''|["'\[{])(.*))$/
998
+ if (val = $4)
999
+ case (ch = $3)
946
1000
  when '{', '['
947
1001
  val = "#{ch}#{val}"
948
1002
  ch = ch == '{' ? '}' : ']'
@@ -955,7 +1009,7 @@ module Squared
955
1009
  end
956
1010
  end
957
1011
  else
958
- val = case (ch = data[2])
1012
+ val = case (ch = $2)
959
1013
  when 'true'
960
1014
  true
961
1015
  when 'false'
@@ -964,7 +1018,10 @@ module Squared
964
1018
  ch.include?('.') ? ch.to_f : ch.to_i
965
1019
  end
966
1020
  end
967
- 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
968
1025
  end
969
1026
  else
970
1027
  found = line.match?(start)
@@ -972,9 +1029,7 @@ module Squared
972
1029
  end
973
1030
  @pyproject[table] = ret
974
1031
  end
975
- return ret.find { |val| val.first == key }&.last if key
976
-
977
- ret
1032
+ key ? ret.find { |val| val.first == key }&.last : ret
978
1033
  end
979
1034
 
980
1035
  def pyprojectfile
@@ -986,9 +1041,9 @@ module Squared
986
1041
  case flag
987
1042
  when :python
988
1043
  /\A(?:v+|q+|b+|V+|O+)\z/
989
- when :pdm
1044
+ when :pdm, :poetry
990
1045
  /\Av+\z/
991
- when :twine
1046
+ when :twine, :meson
992
1047
  nil
993
1048
  else
994
1049
  /\A(?:v+|q+)\z/
@@ -998,7 +1053,7 @@ module Squared
998
1053
  def preopts(quiet: true)
999
1054
  ret = []
1000
1055
  case verbose
1001
- when FalseClass
1056
+ when false
1002
1057
  ret << '--quiet' if quiet
1003
1058
  when Numeric
1004
1059
  ret << "-#{'v' * verbose}" if verbose > 0
@@ -1006,6 +1061,23 @@ module Squared
1006
1061
  ret
1007
1062
  end
1008
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
+
1009
1081
  def variables
1010
1082
  (super + %i[venv editable]).freeze
1011
1083
  end
@@ -1034,11 +1106,16 @@ module Squared
1034
1106
  end
1035
1107
 
1036
1108
  def venv_set(val)
1109
+ @venv = nil
1037
1110
  return unless val
1038
1111
 
1039
- write = ->(level, hint) { log.add(level, "venv: #{@venv}".subhint(hint)) }
1112
+ write = ->(level, hint) { log.add(level, "#{virtualenv? ? 'virtualenv' : 'venv'}: #{@venv}".subhint(hint)) }
1040
1113
  if val.is_a?(Array)
1041
1114
  val, *opts = val
1115
+ if opts.first == 'virtualenv'
1116
+ @virtualenv = true
1117
+ opts.shift
1118
+ end
1042
1119
  @venvopts = opts
1043
1120
  end
1044
1121
  @venv = basepath val
@@ -1058,21 +1135,35 @@ module Squared
1058
1135
  def venv_init
1059
1136
  return if !venv || (venvbin.directory? && !venvbin.empty?)
1060
1137
 
1061
- puts log_message(venv, subject: 'venv', hint: 'init') unless silent?
1062
- opts = @venvopts&.map { |val| OptionPartition.strip(val) }&.flatten
1063
- venv_create(venv, opts || ["prompt=#{name}", 'upgrade-deps'], env: false, banner: false)
1064
- 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?
1065
1143
  end
1066
1144
 
1067
1145
  def venv_create(dir, opts = [], env: nil, banner: banner?)
1068
- cmd, opts = python_session('-m venv', opts: opts)
1069
- op = OptionPartition.new(opts, OPT_PYTHON[:venv], cmd, project: self)
1070
- status = op.append(dir, delim: true)
1071
- .clear(pass: false)
1072
- .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/)
1073
1157
  ret = run(op, env, exception: true, banner: banner)
1074
- pip(:install, 'poetry', banner: false) if poetry?
1075
- 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') }
1076
1167
  end
1077
1168
 
1078
1169
  def pip_install?(flag)
@@ -1089,6 +1180,8 @@ module Squared
1089
1180
  build_backend == 'hatchling.build'
1090
1181
  when :setuptools
1091
1182
  build_backend == 'setuptools.build_meta'
1183
+ when :meson
1184
+ build_backend != 'mesonpy' && exist?('meson.build')
1092
1185
  end
1093
1186
  end
1094
1187
 
@@ -1104,6 +1197,10 @@ module Squared
1104
1197
  dependtype == 1
1105
1198
  end
1106
1199
 
1200
+ def virtualenv?
1201
+ @virtualenv ||= exist?('virtualenv.ini') || !ENV['VIRTUALENV_PYTHON'].nil?
1202
+ end
1203
+
1107
1204
  def requirements?
1108
1205
  dependtype == 5
1109
1206
  end