chemql-python 1.2.4__py3-none-any.whl → 1.2.6__py3-none-any.whl

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.
chemql/__init__.py CHANGED
@@ -1,33 +1,33 @@
1
- import json
2
- import math
3
- import shlex
4
- import os
5
- import re
6
- import ast
7
1
  import argparse
8
- import time
9
- import subprocess
10
- import sys
2
+ import ast
3
+ import atexit
11
4
  import contextlib
5
+ from fractions import Fraction
6
+ from functools import reduce
12
7
  import io
8
+ import json
9
+ import math
10
+ from math import gcd
11
+ import os
13
12
  from pathlib import Path
14
13
  from prompt_toolkit import PromptSession
15
14
  from prompt_toolkit.key_binding import KeyBindings
16
15
  from prompt_toolkit.keys import Keys
17
16
  from prompt_toolkit.lexers import Lexer
18
- import atexit
19
- import uuid
20
- import select
21
- import queue
22
- import threading
23
17
  from prompt_toolkit.styles import Style
24
18
  from prompt_toolkit.completion import Completer, Completion
19
+ import queue
20
+ import re
21
+ import shlex
22
+ import subprocess
23
+ import sys
24
+ import select
25
+ import time
26
+ import threading
27
+ import uuid
25
28
  import urllib.parse
26
29
  import urllib.request
27
30
  import urllib.error
28
- from fractions import Fraction
29
- from functools import reduce
30
- from math import gcd
31
31
 
32
32
 
33
33
  __all__ = [
@@ -47,7 +47,7 @@ __all__ = [
47
47
  "__version__"
48
48
  ]
49
49
 
50
- __version__ = "1.2.4"
50
+ __version__ = "1.2.6"
51
51
 
52
52
  # Persistent preprocessor/function state for the current ChemQL session.
53
53
  DEFINITIONS = {}
@@ -222,11 +222,19 @@ def _call_function(name, arguments):
222
222
 
223
223
  code = line[1:-1].strip()
224
224
 
225
+ # Function parameters must also be substituted in block
226
+ # headers, e.g. [for i in range(2, \1 + 1)].
227
+ resolved_header = (
228
+ "["
229
+ + _expand_definitions(substitute_parameters(code))
230
+ + "]"
231
+ )
232
+
225
233
  if code.startswith("for "):
226
- execute_for_block(line, block_body)
234
+ execute_for_block(resolved_header, block_body)
227
235
 
228
236
  elif code.startswith("while "):
229
- execute_while_block(line, block_body)
237
+ execute_while_block(resolved_header, block_body)
230
238
 
231
239
  elif code.startswith("if "):
232
240
  # Expand parameters and definitions before evaluating.
@@ -367,13 +375,13 @@ _shell_prompt_ready = False
367
375
  _shell_output_streamed = False
368
376
  _shell_output_queue = None
369
377
  _shell_reader_thread = None
378
+ _shell_script_path = None
370
379
  _shell_prompt = f"__CHEMQL_PROMPT_{uuid.uuid4().hex}__"
371
380
 
372
-
373
381
  def get_shell():
374
382
  """Return the persistent OS shell process for the current platform."""
375
383
  global _shell_process, _shell_master_fd, _shell_prompt_ready
376
- global _shell_output_queue, _shell_reader_thread
384
+ global _shell_output_queue, _shell_reader_thread, _shell_script_path
377
385
 
378
386
  if _shell_process is not None and _shell_process.poll() is None:
379
387
  return _shell_process
@@ -382,20 +390,89 @@ def get_shell():
382
390
  close_shell()
383
391
 
384
392
  if os.name == "nt":
385
- # Use PowerShell as ChemQL's native Windows shell. Its prompt function
386
- # emits a private marker when a command finishes, so we do not append a
387
- # marker to stdin (which would accidentally become Read-Host input).
388
- setup = (
389
- f"function global:prompt {{ [Console]::Out.WriteLine('{_shell_prompt}'); return '' }}"
393
+ # PowerShell reads its program from a file, leaving stdin available for
394
+ # the command/marker protocol and commands such as Read-Host.
395
+ import tempfile
396
+
397
+ setup = r'''$ErrorActionPreference = 'Continue'
398
+ [Console]::InputEncoding = New-Object System.Text.UTF8Encoding($false)
399
+ [Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
400
+ $OutputEncoding = New-Object System.Text.UTF8Encoding($false)
401
+ [Console]::Out.WriteLine('__CHEMQL_READY__')
402
+ [Console]::Out.Flush()
403
+
404
+ while ($true) {
405
+ # One physical input line per request avoids command/marker line desync.
406
+ $request = [Console]::In.ReadLine()
407
+ if ($null -eq $request) { break }
408
+
409
+ $separatorIndex = $request.IndexOf('|')
410
+ if ($separatorIndex -le 0) {
411
+ [Console]::Out.WriteLine('CHEMQL_PROTOCOL_ERROR: malformed request')
412
+ [Console]::Out.Flush()
413
+ continue
414
+ }
415
+
416
+ $marker = $request.Substring(0, $separatorIndex)
417
+ $encodedCommand = $request.Substring($separatorIndex + 1)
418
+
419
+ try {
420
+ $commandText = [System.Text.Encoding]::UTF8.GetString(
421
+ [System.Convert]::FromBase64String($encodedCommand)
390
422
  )
423
+ if ([string]::IsNullOrWhiteSpace($commandText)) {
424
+ [Console]::Out.WriteLine('ChemQL received an empty PowerShell command.')
425
+ }
426
+ else {
427
+ # Invoke-Expression runs in this persistent script scope, preserving
428
+ # PowerShell variables between ChemQL requests. Format pipeline
429
+ # output explicitly for each request; otherwise PowerShell's default
430
+ # formatting subsystem can defer table output until the next request,
431
+ # making commands such as Get-Location appear blank or duplicated.
432
+ $formattedOutput = Invoke-Expression -Command $commandText | Out-String -Width 4096
433
+ if (-not [string]::IsNullOrEmpty($formattedOutput)) {
434
+ [Console]::Out.Write($formattedOutput)
435
+ }
436
+ }
437
+ }
438
+ catch {
439
+ [Console]::Out.WriteLine($_.ToString())
440
+ }
441
+ finally {
442
+ [Console]::Out.WriteLine($marker)
443
+ [Console]::Out.Flush()
444
+ }
445
+ }
446
+ ''' # I have no idea what this is, this is code I wrote today at 4:00AM but it somehow works
447
+ # reminder to future self to not touch this
448
+ fd, _shell_script_path = tempfile.mkstemp(prefix="chemql_shell_", suffix=".ps1")
449
+ try:
450
+ with os.fdopen(fd, "w", encoding="ascii", newline="\n") as script_file:
451
+ script_file.write(setup)
452
+ except Exception:
453
+ try:
454
+ os.close(fd)
455
+ except OSError:
456
+ pass
457
+ try:
458
+ os.unlink(_shell_script_path)
459
+ except OSError:
460
+ pass
461
+ _shell_script_path = None
462
+ raise
463
+
391
464
  _shell_process = subprocess.Popen(
392
- ["powershell.exe", "-NoLogo", "-NoProfile", "-NoExit", "-Command", setup],
465
+ ["powershell.exe", "-NoLogo", "-NoProfile",
466
+ "-ExecutionPolicy", "Bypass", "-File", _shell_script_path],
393
467
  stdin=subprocess.PIPE,
394
468
  stdout=subprocess.PIPE,
395
469
  stderr=subprocess.STDOUT,
396
470
  text=True,
471
+ encoding="utf-8",
472
+ errors="replace",
397
473
  bufsize=1,
398
474
  )
475
+
399
476
  output_queue = queue.Queue()
400
477
  _shell_output_queue = output_queue
401
478
  process = _shell_process
@@ -414,30 +491,67 @@ def get_shell():
414
491
 
415
492
  _shell_reader_thread = threading.Thread(target=read_output, daemon=True)
416
493
  _shell_reader_thread.start()
494
+
495
+ # Wait briefly for the PowerShell script to confirm it started. This
496
+ # prevents the first request from racing process startup and diagnoses
497
+ # startup failures instead of hanging later while waiting for a marker.
498
+ ready_text = ""
499
+ ready_deadline = time.monotonic() + 5
500
+ while "__CHEMQL_READY__" not in ready_text:
501
+ remaining = ready_deadline - time.monotonic()
502
+ if remaining <= 0:
503
+ startup_output = ready_text.replace("\\r\\n", "\\n").strip()
504
+ close_shell()
505
+ raise RuntimeError(
506
+ "PowerShell request loop did not start within 5 seconds."
507
+ + (f" Output: {startup_output}" if startup_output else "")
508
+ )
509
+ try:
510
+ character = output_queue.get(timeout=min(0.1, remaining))
511
+ except queue.Empty:
512
+ if _shell_process.poll() is not None:
513
+ raise RuntimeError(
514
+ "PowerShell exited during startup with code "
515
+ f"{_shell_process.returncode}."
516
+ )
517
+ continue
518
+ if character is None:
519
+ raise RuntimeError("PowerShell closed its output during startup.")
520
+ ready_text += character
521
+ ready_pos = ready_text.find("__CHEMQL_READY__")
522
+ # Any text before the readiness token is startup diagnostics. It is
523
+ # not command output and should not leak into the first request.
524
+ startup_extra = ready_text[:ready_pos]
525
+ if startup_extra.strip():
526
+ # Preserve startup diagnostics for the next request to display.
527
+ for char in startup_extra:
528
+ output_queue.put(char)
529
+
530
+ # Windows PowerShell uses the explicit command/marker protocol above.
531
+ # Do not run the POSIX PTY prompt handshake below: it waits for a shell
532
+ # prompt that this non-interactive PowerShell script intentionally has
533
+ # disabled, causing get_shell() to hang before the first command.
534
+ return _shell_process
535
+
417
536
  else:
537
+ # Leave the existing POSIX implementation unchanged.
418
538
  import fcntl
419
539
  import pty
420
540
  import termios
421
541
 
422
542
  master_fd, slave_fd = pty.openpty()
423
- # Don't echo bytes sent by ChemQL or keyboard input twice. Keep ISIG
424
- # enabled so forwarding Ctrl-C to the PTY interrupts the foreground job.
425
543
  attrs = termios.tcgetattr(slave_fd)
426
544
  attrs[3] &= ~termios.ECHO
427
545
  termios.tcsetattr(slave_fd, termios.TCSANOW, attrs)
428
546
 
429
547
  def attach_controlling_terminal():
430
- # Popen(start_new_session=True) has already called setsid().
431
548
  fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0)
432
549
 
433
550
  try:
434
551
  _shell_process = subprocess.Popen(
435
552
  ["bash", "--noprofile", "--norc"],
436
- stdin=slave_fd,
437
- stdout=slave_fd,
438
- stderr=slave_fd,
439
- close_fds=True,
440
- start_new_session=True,
553
+ stdin=slave_fd, stdout=slave_fd, stderr=slave_fd,
554
+ close_fds=True, start_new_session=True,
441
555
  preexec_fn=attach_controlling_terminal,
442
556
  env={**os.environ, "PS1": _shell_prompt, "PS2": ""},
443
557
  )
@@ -451,7 +565,6 @@ def get_shell():
451
565
 
452
566
  return _shell_process
453
567
 
454
-
455
568
  def _run_shell_command_posix(command, marker=None):
456
569
  """Run a command through the persistent PTY and forward keyboard input.
457
570
 
@@ -469,6 +582,7 @@ def _run_shell_command_posix(command, marker=None):
469
582
  prompt_bytes = _shell_prompt.encode()
470
583
 
471
584
  def wait_for_prompt(forward_keyboard=False):
585
+ global _shell_output_streamed
472
586
  collected = bytearray()
473
587
  streamed_pos = 0
474
588
  stdin_fd = None
@@ -481,7 +595,7 @@ def _run_shell_command_posix(command, marker=None):
481
595
  while True:
482
596
  readers = [master_fd]
483
597
  if stdin_fd is not None:
484
- readers.append(stdin_fd)
598
+ readers.append(stdin_fd) #type:ignore
485
599
  try:
486
600
  ready, _, _ = select.select(readers, [], [])
487
601
  except KeyboardInterrupt:
@@ -562,129 +676,178 @@ def _run_shell_command_posix(command, marker=None):
562
676
  close_shell()
563
677
  return f"Shell error: {exc}"
564
678
 
565
-
566
679
  def _run_shell_command_windows(command):
567
- """Run a command in persistent PowerShell and forward line input interactively."""
568
- global _shell_prompt_ready, _shell_output_streamed
680
+ """Execute one command in the persistent PowerShell request loop."""
681
+ import base64
682
+ import msvcrt
683
+ import uuid
684
+
569
685
  shell = get_shell()
570
686
  output_queue = _shell_output_queue
571
687
  if output_queue is None:
572
688
  return "Shell error: PowerShell output reader was not initialized."
689
+ if shell.stdin is None:
690
+ return "Shell error: PowerShell stdin is unavailable."
573
691
 
574
- import msvcrt
692
+ marker = f"__CHEMQL_DONE_{uuid.uuid4().hex}__"
693
+ encoded_command = base64.b64encode(command.encode("utf-8")).decode("ascii")
694
+ timeout_seconds=120
695
+ def wait_for_marker(timeout_seconds=120):
696
+ """Collect output while streaming prompts so interactive commands remain usable."""
697
+ global _shell_output_streamed
575
698
 
576
- def wait_for_prompt(stream_output):
577
699
  collected = ""
578
700
  streamed_pos = 0
579
- marker = _shell_prompt
580
- marker_len = len(marker)
581
- stdin_is_tty = False
701
+ started = time.monotonic()
582
702
  try:
583
703
  stdin_is_tty = sys.stdin is not None and sys.stdin.isatty()
584
704
  except (AttributeError, OSError, ValueError):
585
- pass
705
+ stdin_is_tty = False
586
706
  pending_input = ""
587
707
 
708
+ def stream_until(end):
709
+ nonlocal streamed_pos
710
+ global _shell_output_streamed
711
+ if not stdin_is_tty or end <= streamed_pos:
712
+ return
713
+ visible = collected[streamed_pos:end]
714
+ if visible:
715
+ try:
716
+ sys.stdout.write(visible)
717
+ sys.stdout.flush()
718
+ _shell_output_streamed = True
719
+ except (OSError, UnicodeError):
720
+ pass
721
+ streamed_pos = end
722
+
588
723
  while True:
589
- # While a shell command is running, pass complete lines typed by the
590
- # user to PowerShell. This lets Read-Host receive real input rather
591
- # than consuming a completion marker injected by ChemQL.
724
+ if time.monotonic() - started > timeout_seconds:
725
+ return (None, collected, True)
726
+
592
727
  if stdin_is_tty:
593
728
  try:
594
- while msvcrt.kbhit(): #type:ignore
595
- key = msvcrt.getwch() #type:ignore
596
- if key in ("\x00", "\xe0"): #type:ignore
597
- # Consume the second half of a Windows special key.
598
- if msvcrt.kbhit(): #type:ignore
599
- msvcrt.getwch() #type:ignore
729
+ while msvcrt.kbhit(): # type: ignore
730
+ key = msvcrt.getwch() # type: ignore
731
+ if key in ("\x00", "\xe0"):
732
+ if msvcrt.kbhit(): # type: ignore
733
+ msvcrt.getwch() # type: ignore
600
734
  elif key in ("\r", "\n"):
601
- shell.stdin.write(pending_input + "\r\n") # type: ignore
735
+ shell.stdin.write(pending_input + "\n") # type: ignore # text mode normalizes newlines on Windows
602
736
  shell.stdin.flush() # type: ignore
737
+ sys.stdout.write("\n")
738
+ sys.stdout.flush()
603
739
  pending_input = ""
604
740
  elif key == "\b":
605
- pending_input = pending_input[:-1]
741
+ if pending_input:
742
+ pending_input = pending_input[:-1]
743
+ sys.stdout.write("\b \b")
744
+ sys.stdout.flush()
606
745
  elif key == "\x03":
607
- # Ctrl-C is sent as a line input; reset is safer than
608
- # leaving ChemQL stuck in a nested interactive prompt.
609
746
  close_shell()
610
- return None
611
- elif key >= " " and key != "\x7f":
747
+ return (None, collected, False)
748
+ elif key >= " " and key != "\x7f": # I have no idea what I did but it workd
612
749
  pending_input += key
750
+ sys.stdout.write(key)
751
+ sys.stdout.flush()
613
752
  except (OSError, ValueError):
614
753
  stdin_is_tty = False
615
754
 
616
755
  try:
617
756
  character = output_queue.get(timeout=0.04)
618
757
  except queue.Empty:
758
+ if shell.poll() is not None:
759
+ return (None, collected, False)
619
760
  continue
620
761
  if character is None:
621
- return None
762
+ return (None, collected, False)
622
763
 
623
764
  collected += character
624
765
  marker_pos = collected.find(marker)
625
766
  if marker_pos >= 0:
767
+ stream_until(marker_pos)
626
768
  visible = collected[:marker_pos]
627
- if stream_output and marker_pos > streamed_pos:
628
- sys.stdout.write(collected[streamed_pos:marker_pos])
629
- sys.stdout.flush()
630
- _shell_output_streamed = True
631
- return visible.replace("\r\n", "\n").rstrip("\r\n ")
632
-
633
- if stream_output:
634
- # Stream immediately, retaining the full output for history and
635
- # only withholding a suffix that could begin the prompt marker.
636
- overlap = 0
637
- max_overlap = min(marker_len - 1, len(collected))
638
- for size in range(max_overlap, 0, -1):
639
- if collected.endswith(marker[:size]):
640
- overlap = size
641
- break
642
- safe_end = len(collected) - overlap
643
- if safe_end > streamed_pos:
644
- sys.stdout.write(collected[streamed_pos:safe_end])
645
- sys.stdout.flush()
646
- _shell_output_streamed = True
647
- streamed_pos = safe_end
769
+ return (
770
+ visible.replace("\r\n", "\n").rstrip("\r\n "),
771
+ collected,
772
+ False,
773
+ )
648
774
 
649
- if not _shell_prompt_ready:
650
- initial = wait_for_prompt(stream_output=False)
651
- if initial is None:
652
- close_shell()
653
- return "Shell session terminated."
654
- _shell_prompt_ready = True
775
+ # Hold back a suffix that could be the beginning of the completion
776
+ # marker, so protocol text is never accidentally printed.
777
+ overlap = min(len(marker) - 1, len(collected))
778
+ while overlap and not collected.endswith(marker[:overlap]):
779
+ overlap -= 1
780
+ stream_until(len(collected) - overlap)
655
781
 
656
782
  try:
657
- shell.stdin.write(command.replace("\n", "\r\n") + "\r\n") # type: ignore
658
- shell.stdin.flush() # type: ignore
659
- result = wait_for_prompt(stream_output=True)
783
+ shell.stdin.write(marker + "|" + encoded_command + "\n") # text mode normalizes newlines on Windows
784
+ shell.stdin.flush()
785
+ result, collected, timed_out = wait_for_marker()
786
+ if timed_out:
787
+ close_shell()
788
+ debug = collected.replace("\r\n", "\n").rstrip()
789
+ detail = f"PowerShell did not return its completion marker within {timeout_seconds} seconds."
790
+ if debug:
791
+ detail += f"\nOutput received before timeout:\n{debug}"
792
+ else:
793
+ detail += "\nNo output was received. The PowerShell request loop may not have started or may not be reading stdin."
794
+ return detail
660
795
  if result is None:
661
796
  close_shell()
662
- return "Shell session terminated."
797
+ return "Shell session terminated before PowerShell returned its completion marker."
663
798
  return result
664
799
  except (BrokenPipeError, OSError, ValueError) as exc:
665
800
  close_shell()
666
801
  return f"Shell error: {exc}"
667
802
 
668
-
669
803
  def run_shell_command(command):
670
804
  global _shell_output_streamed
671
- command = command.replace("\r\n", "\n").replace("\r", "\n").rstrip("\n")
805
+
806
+ command = (
807
+ command.replace("\r\n", "\n")
808
+ .replace("\r", "\n")
809
+ .rstrip("\n")
810
+ )
811
+
672
812
  try:
673
813
  if os.name == "nt":
674
- if command.strip().casefold() in {"powershell", "powershell.exe", "pwsh", "pwsh.exe"}:
675
- return "ChemQL already uses PowerShell on Windows; run the command directly instead of starting a nested shell."
814
+ stripped = command.strip()
815
+
816
+ if stripped.casefold() in {
817
+ "powershell",
818
+ "powershell.exe",
819
+ "pwsh",
820
+ "pwsh.exe",
821
+ }:
822
+ return (
823
+ "ChemQL already uses PowerShell on Windows; "
824
+ "run the command directly instead of starting "
825
+ "a nested shell."
826
+ )
827
+
828
+ # Clear the terminal without sending Clear-Host to PowerShell.
829
+ if stripped.casefold() in {
830
+ "clear",
831
+ "cls",
832
+ "clear-host",
833
+ }:
834
+ os.system("cls")
835
+ return ""
836
+
676
837
  _shell_output_streamed = False
677
838
  return _run_shell_command_windows(command)
839
+
840
+ # POSIX behavior remains unchanged.
678
841
  return _run_shell_command_posix(command)
842
+
679
843
  except KeyboardInterrupt:
680
844
  close_shell()
681
845
  return "Shell command interrupted. Shell session was reset."
682
846
 
683
-
684
847
  def close_shell():
685
848
  """Clean up the persistent shell and its pseudo-terminal on exit."""
686
849
  global _shell_process, _shell_master_fd, _shell_prompt_ready
687
- global _shell_output_queue, _shell_reader_thread
850
+ global _shell_output_queue, _shell_reader_thread, _shell_script_path
688
851
 
689
852
  if _shell_process is not None:
690
853
  if _shell_process.poll() is None:
@@ -709,6 +872,13 @@ def close_shell():
709
872
  _shell_output_queue = None
710
873
  _shell_reader_thread = None
711
874
 
875
+ if _shell_script_path is not None:
876
+ try:
877
+ os.unlink(_shell_script_path)
878
+ except OSError:
879
+ pass
880
+ _shell_script_path = None
881
+
712
882
 
713
883
  atexit.register(close_shell)
714
884
 
@@ -2317,6 +2487,24 @@ def _space_query_operators(text):
2317
2487
  out.append(ch)
2318
2488
  i += 1
2319
2489
  continue
2490
+ # Preserve the compound operator "like!" as a single token.
2491
+ if text[i:i + 5].casefold() == "like!":
2492
+ before_ok = (
2493
+ i == 0 or not (
2494
+ text[i - 1].isalnum() or text[i - 1] == "_"
2495
+ )
2496
+ )
2497
+ end = i + 5
2498
+ after_ok = (
2499
+ end == len(text) or not (
2500
+ text[end].isalnum() or text[end] == "_"
2501
+ )
2502
+ )
2503
+
2504
+ if before_ok and after_ok:
2505
+ out.extend((" ", text[i:end], " "))
2506
+ i = end
2507
+ continue
2320
2508
  if ch in "()":
2321
2509
  out.extend((" ", ch, " "))
2322
2510
  i += 1
@@ -3111,7 +3299,7 @@ def process_lines(lines):
3111
3299
  result = strip_ansi(result) if NO_ANSI else result
3112
3300
  # POSIX PTY commands stream output live. Never print their returned
3113
3301
  # capture a second time; Windows pipe mode still needs normal printing.
3114
- if result and (os.name == "nt" or not _shell_output_streamed):
3302
+ if result and not _shell_output_streamed:
3115
3303
  print(result)
3116
3304
  _record_execution("```\n" + command + "\n```", result, not result.startswith("Shell error:"))
3117
3305
  globals()["_shell_output_streamed"] = False
@@ -3526,10 +3714,13 @@ def execute(arguments, query_text=None, positions=None):
3526
3714
  # Convenience aliases for very common lookups.
3527
3715
  # These are syntactic sugar for normal search queries.
3528
3716
  if arguments[0] == "findel":
3529
- if len(arguments) != 2:
3717
+ if len(arguments) < 2:
3530
3718
  return "Usage: findel <name-or-symbol>"
3531
3719
 
3532
3720
  value = arguments[1]
3721
+
3722
+ return_attributes = None
3723
+
3533
3724
  return execute(
3534
3725
  ["search", "elements", "name", "like", value, "or", "symbol", "like", value]
3535
3726
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chemql-python
3
- Version: 1.2.4
3
+ Version: 1.2.6
4
4
  Summary: A chemistry-focused query language and REPL
5
5
  Author-email: Mohammad Rakibul Islam <rakibulofficial1017@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -17,15 +17,15 @@ Dynamic: license-file
17
17
  shell commands in one interactive environment.**
18
18
 
19
19
  ChemQL is a command-line chemistry query language for exploring chemical
20
- elements, molecules, and reactions. Version 1.2.3 combines database-style
20
+ elements, molecules, and reactions. Version 1.2.6 combines database-style
21
21
  chemistry queries, reaction tools, Python interoperability, reusable functions
22
22
  and macros, and a persistent shell in one scriptable environment. It combines a small SQL-inspired
23
23
  query syntax with a persistent Python environment, reaction utilities,
24
24
  optional external molecule lookup, and tools for running repeatable
25
25
  scripts.
26
26
 
27
- > **Version covered by this README:** 1.2.3\
28
- > The examples below describe the ChemQL 1.2.3 source. Dataset
27
+ > **Version covered by this README:** 1.2.6\
28
+ > The examples below describe the ChemQL 1.2.6 source. Dataset
29
29
  > contents and external-service availability can affect individual
30
30
  > lookup results.
31
31
 
@@ -538,7 +538,7 @@ queried or inspected from Python.
538
538
 
539
539
  ## Preprocessor, functions, and macros
540
540
 
541
- ChemQL 1.2.3 adds three ways to reuse values and logic. These declarations
541
+ ChemQL 1.2.6 adds three ways to reuse values and logic. These declarations
542
542
  are processed in source order: definitions affect later lines, not earlier
543
543
  ones. They persist during the current ChemQL process/session; they are not
544
544
  saved automatically between separate launches.
@@ -833,7 +833,7 @@ redirect a file into the command.
833
833
  ``` bash
834
834
  chemql --version
835
835
  chemql --upgrade
836
- chemql --upgrade 1.2.3
836
+ chemql --upgrade 1.2.6
837
837
  ```
838
838
 
839
839
  `--upgrade` installs the latest published `chemql-python` release; supplying a
@@ -1,4 +1,4 @@
1
- chemql/__init__.py,sha256=IOUsPMXZyM6zbMu0Z5TEu0PuwQkYGEFF3oSVKJQXz0c,160298
1
+ chemql/__init__.py,sha256=6bmYNdAj5SEZzsrKVtmVUqn5PB-XSIT4bkMmj4BjY_0,167346
2
2
  chemql/__main__.py,sha256=3Q52s0hyuouhvSSqFEQFDr6mtJ-azkCwI2AD4c7f7A8,28
3
3
  chemql/data/BondEnergies.json,sha256=QZN3iWZrD8125jV41EG1GiRohBtP0CzAP_EDQTfaEKc,8269
4
4
  chemql/data/MoleculeBonds.json,sha256=Iw2shYxJScH_tI80IytBYoqVDSbgT7OSqPJUIgKZme4,84262
@@ -8,9 +8,9 @@ chemql/data/Reactions.json,sha256=BFpkKEWwLzG54ot96BVOWwU3btpimRJlOM57yGclGgc,15
8
8
  chemql/data/THIRD_PARTY_LICENSES/PeriodicTableJSON.txt,sha256=9wih5q8s58qR3vJEGpooa4ESyAGZHcmGvzwMMkMHNKk,322
9
9
  chemql/data/THIRD_PARTY_LICENSES/RHEA.txt,sha256=nXTeCP6vJBW5B_mtaVGFLr0w7vnjrSTBnv6_eOk3KKo,634
10
10
  chemql/html/periodic_table.html,sha256=ftVgXWjRO2AsTAGEekTrvgE5ZvcwRfoby9UWoixc9Y8,361560
11
- chemql_python-1.2.4.dist-info/licenses/LICENSE,sha256=o4ywHC0EwRy2zgIASiRD2sK3Vcsf7LBcTHjDvoJgRE0,1079
12
- chemql_python-1.2.4.dist-info/METADATA,sha256=RK2Pp080yqIdLxYD8cXzzWxUqXbXhvFBNuob6Z0b-38,30529
13
- chemql_python-1.2.4.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
- chemql_python-1.2.4.dist-info/entry_points.txt,sha256=j98ObVWR743an524z24AiWeRsppag-py5cc7l0D0HkI,39
15
- chemql_python-1.2.4.dist-info/top_level.txt,sha256=9UzHwnk_F6W19Q9nayaFgVyAELQXXwoYjeBgB6MWNr8,7
16
- chemql_python-1.2.4.dist-info/RECORD,,
11
+ chemql_python-1.2.6.dist-info/licenses/LICENSE,sha256=o4ywHC0EwRy2zgIASiRD2sK3Vcsf7LBcTHjDvoJgRE0,1079
12
+ chemql_python-1.2.6.dist-info/METADATA,sha256=KENM4stpgADfHjbgSEBkNZLSI4a6AWOAylgUnl4wMAw,30529
13
+ chemql_python-1.2.6.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
+ chemql_python-1.2.6.dist-info/entry_points.txt,sha256=j98ObVWR743an524z24AiWeRsppag-py5cc7l0D0HkI,39
15
+ chemql_python-1.2.6.dist-info/top_level.txt,sha256=9UzHwnk_F6W19Q9nayaFgVyAELQXXwoYjeBgB6MWNr8,7
16
+ chemql_python-1.2.6.dist-info/RECORD,,