jsonata-python 0.5.3__py3-none-any.whl → 0.6.1__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.
jsonata/cli/__main__.py CHANGED
@@ -32,9 +32,9 @@ from jsonata import functions, jexception, jsonata, timebox
32
32
  def get_options(argv: Optional[list[str]] = None) -> argparse.ArgumentParser:
33
33
  """Parses command-line arguments.
34
34
  """
35
- parser = argparse.ArgumentParser(prog="jsonata.cli", description="Pure Python JSONata CLI")
35
+ parser = argparse.ArgumentParser(prog="jsonata", description="Pure Python JSONata CLI")
36
36
  parser.add_argument(
37
- "-v", "--version", action='version', version='%(prog)s 0.5.3')
37
+ "-v", "--version", action='version', version='%(prog)s 0.6.1')
38
38
 
39
39
  parser.add_argument(
40
40
  "-e", "--expression", metavar="<file>",
jsonata/functions.py CHANGED
@@ -441,6 +441,13 @@ class Functions:
441
441
  if char is None or not char:
442
442
  char = " "
443
443
 
444
+ # match JS: truncate width to integer
445
+ if width is not None:
446
+ try:
447
+ width = int(width)
448
+ except Exception:
449
+ width = 0
450
+
444
451
  if width < 0:
445
452
  result = Functions.left_pad(string, -width, char)
446
453
  else:
@@ -722,7 +729,7 @@ class Functions:
722
729
  r = None
723
730
  for i in range(0, 10):
724
731
  try:
725
- r = re.sub(pattern, replacement, s, 1)
732
+ r = re.sub(pattern, replacement, s, count=1)
726
733
  break
727
734
  except Exception as e:
728
735
  msg = str(e)
@@ -2098,7 +2105,8 @@ class Functions:
2098
2105
  dt = datetime.datetime.strptime(timestamp, "%Y")
2099
2106
  else:
2100
2107
  dt = datetime.datetime.fromisoformat(timestamp)
2101
- dt = dt.replace(tzinfo=datetime.timezone.utc)
2108
+ if dt.tzinfo is None:
2109
+ dt = dt.replace(tzinfo=datetime.timezone.utc)
2102
2110
  return int(dt.timestamp() * 1000)
2103
2111
  # try:
2104
2112
  # size = len(timestamp)
jsonata/jsonata.py CHANGED
@@ -800,9 +800,9 @@ class Jsonata:
800
800
 
801
801
  result = None
802
802
  if op == "=":
803
- result = lhs == rhs # isDeepEqual(lhs, rhs);
803
+ result = utils.Utils.is_deep_equal(lhs, rhs)
804
804
  elif op == "!=":
805
- result = lhs != rhs # !isDeepEqual(lhs, rhs);
805
+ result = not utils.Utils.is_deep_equal(lhs, rhs)
806
806
  return result
807
807
 
808
808
  #
jsonata/parser.py CHANGED
@@ -529,6 +529,12 @@ class Parser:
529
529
  # if/then/else ternary operator ?:
530
530
  self.register(Parser.InfixTernaryOperator(self, tokenizer.Tokenizer.operators["?"]))
531
531
 
532
+ # coalescing operator ??
533
+ self.register(Parser.InfixCoalesce(self, tokenizer.Tokenizer.operators["??"]))
534
+
535
+ # elvis/default operator ?:
536
+ self.register(Parser.InfixDefault(self, tokenizer.Tokenizer.operators["?:"]))
537
+
532
538
  # object transformer
533
539
  self.register(Parser.PrefixObjectTransformer(self))
534
540
 
@@ -854,6 +860,43 @@ class Parser:
854
860
  self._else = self._outer_instance.expression(0)
855
861
  return self
856
862
 
863
+ class InfixCoalesce(Infix):
864
+ _outer_instance: 'Parser'
865
+
866
+ def __init__(self, outer_instance, get):
867
+ super().__init__(outer_instance, "??", get)
868
+ self._outer_instance = outer_instance
869
+
870
+ def led(self, left):
871
+ self.type = "condition"
872
+ # condition becomes function exists(left)
873
+ cond = Parser.Symbol(self._outer_instance)
874
+ cond.type = "function"
875
+ cond.value = "("
876
+ proc = Parser.Symbol(self._outer_instance)
877
+ proc.type = "variable"
878
+ proc.value = "exists"
879
+ cond.procedure = proc
880
+ cond.arguments = [left]
881
+ self.condition = cond
882
+ self.then = left
883
+ self._else = self._outer_instance.expression(0)
884
+ return self
885
+
886
+ class InfixDefault(Infix):
887
+ _outer_instance: 'Parser'
888
+
889
+ def __init__(self, outer_instance, get):
890
+ super().__init__(outer_instance, "?:", get)
891
+ self._outer_instance = outer_instance
892
+
893
+ def led(self, left):
894
+ self.type = "condition"
895
+ self.condition = left
896
+ self.then = left
897
+ self._else = self._outer_instance.expression(0)
898
+ return self
899
+
857
900
  class PrefixObjectTransformer(Prefix):
858
901
  _outer_instance: 'Parser'
859
902
 
jsonata/tokenizer.py CHANGED
@@ -65,6 +65,8 @@ class Tokenizer:
65
65
  '<=': 40,
66
66
  '>=': 40,
67
67
  '~>': 40,
68
+ '?:': 40,
69
+ '??': 40,
68
70
  'and': 30,
69
71
  'or': 25,
70
72
  'in': 40,
@@ -216,6 +218,14 @@ class Tokenizer:
216
218
  # ~> chain function
217
219
  self.position += 2
218
220
  return self.create("operator", "~>")
221
+ if current_char == '?' and have_more and self.path[self.position + 1] == ':':
222
+ # ?: default / elvis operator
223
+ self.position += 2
224
+ return self.create("operator", "?:")
225
+ if current_char == '?' and have_more and self.path[self.position + 1] == '?':
226
+ # ?? coalescing operator
227
+ self.position += 2
228
+ return self.create("operator", "??")
219
229
  # test for single char operators
220
230
  if Tokenizer.operators.get(str(current_char)) is not None:
221
231
  self.position += 1
jsonata/utils.py CHANGED
@@ -90,6 +90,27 @@ class Utils:
90
90
  sequence.sequence = True
91
91
  return sequence
92
92
 
93
+ @staticmethod
94
+ def is_deep_equal(lhs: Optional[Any], rhs: Optional[Any]) -> bool:
95
+ if isinstance(lhs, list) and isinstance(rhs, list):
96
+ if len(lhs) != len(rhs):
97
+ return False
98
+ for ii, _ in enumerate(lhs):
99
+ if not Utils.is_deep_equal(lhs[ii], rhs[ii]):
100
+ return False
101
+ return True
102
+ elif isinstance(lhs, dict) and isinstance(rhs, dict):
103
+ if lhs.keys() != rhs.keys():
104
+ return False
105
+ for key in lhs.keys():
106
+ if not Utils.is_deep_equal(lhs[key], rhs[key]):
107
+ return False
108
+ return True
109
+ if lhs == rhs and type(lhs) == type(rhs):
110
+ return True
111
+
112
+ return False
113
+
93
114
  class JList(list):
94
115
  sequence: bool
95
116
  outer_wrapper: bool
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsonata-python
3
- Version: 0.5.3
3
+ Version: 0.6.1
4
4
  Summary: Pure Python implementation of JSONata
5
5
  Project-URL: Homepage, https://github.com/rayokota/jsonata-python
6
6
  Project-URL: Bug Reports, https://github.com/rayokota/jsonata-python/issues
@@ -235,7 +235,7 @@ The JSONata documentation can be found [here](https://jsonata.org).
235
235
  ## Installation
236
236
 
237
237
  ```
238
- pip install jsonata-python
238
+ pipx install jsonata-python
239
239
  ```
240
240
 
241
241
  ## Getting Started
@@ -256,8 +256,8 @@ A very simple start:
256
256
  The CLI provides the same functionality as the [Dashjoin JSONata CLI](https://github.com/dashjoin/jsonata-cli).
257
257
 
258
258
  ```
259
- % python3 -m jsonata.cli
260
- usage: jsonata.cli [-h] [-v] [-e <file>] [-i <arg>] [-ic <arg>] [-f {auto,json,string}] [-o <arg>] [-oc <arg>] [-time] [-c] [-b <json-string>]
259
+ % jsonata -h
260
+ usage: jsonata [-h] [-v] [-e <file>] [-i <arg>] [-ic <arg>] [-f {auto,json,string}] [-o <arg>] [-oc <arg>] [-time] [-c] [-b <json-string>]
261
261
  [-bf <file>] [-it]
262
262
  [expr]
263
263
 
@@ -293,19 +293,19 @@ options:
293
293
  ### Examples
294
294
 
295
295
  ```
296
- % echo '{"a":"hello", "b":" world"}' | python3 -m jsonata.cli '(a & b)'
296
+ % echo '{"a":"hello", "b":" world"}' | jsonata '(a & b)'
297
297
  hello world
298
298
 
299
- % echo '{"a":"hello", "b":" world"}' | python3 -m jsonata.cli -o helloworld.json $
299
+ % echo '{"a":"hello", "b":" world"}' | jsonata -o helloworld.json $
300
300
  # helloworld.json written
301
301
 
302
- % ls | python3 -m jsonata.cli $
302
+ % ls | jsonata $
303
303
  helloworld.json
304
304
 
305
- % ps -o pid="",%cpu="",%mem="" | python3 -m jsonata.cli '$.$split(/\n/).$trim().[ $split(/\s+/)[$length()>0].$number() ]' -c
305
+ % ps -o pid="",%cpu="",%mem="" | jsonata '$.$split(/\n/).$trim().[ $split(/\s+/)[$length()>0].$number() ]' -c
306
306
  [[4105,0,0],[4646,0,0],[4666,0,0],[33696,0,0]...]
307
307
 
308
- % curl -s https://raw.githubusercontent.com/jsonata-js/jsonata/master/test/test-suite/datasets/dataset1.json | python3 -m jsonata.cli '{"Name": FirstName & " " & Surname, "Cities": **.City, "Emails": Email[type="home"].address}'
308
+ % curl -s https://raw.githubusercontent.com/jsonata-js/jsonata/master/test/test-suite/datasets/dataset1.json | jsonata '{"Name": FirstName & " " & Surname, "Cities": **.City, "Emails": Email[type="home"].address}'
309
309
  {
310
310
  "Name": "Fred Smith",
311
311
  "Cities": [
@@ -318,7 +318,7 @@ helloworld.json
318
318
  ]
319
319
  }
320
320
 
321
- % python3 -m jsonata.cli -i helloworld.json -it
321
+ % jsonata -i helloworld.json -it
322
322
  Enter an expression to have it evaluated.
323
323
  JSONata> (a & b)
324
324
  hello world
@@ -0,0 +1,18 @@
1
+ jsonata/__init__.py,sha256=4r8USHj4SoBy_TD8dLxt9HJIgpLfXApFrZBipi4rgr0,388
2
+ jsonata/constants.py,sha256=WtdH_l_s5KD-SiOJ4GR2az7WpVgKB2HguXUnyfy4tvs,3017
3
+ jsonata/datetimeutils.py,sha256=IzU6y-vwyhwobHyw9rsImBye-RfWDV7K8Vk0Xs-yiG0,48567
4
+ jsonata/functions.py,sha256=it09GOcMOJ2XDlvh_yhf-bgABaHMTrUFhzXa0kUsrNk,75729
5
+ jsonata/jexception.py,sha256=6Jz7WMsIiNlQ7-1Hq8RKiE2HxcHq2PDekw0qsSe3lqo,12885
6
+ jsonata/jsonata.py,sha256=YmLrus5eBOdcGIzV6nEzsFOkBRJvosE-3bjpdQZVISg,84248
7
+ jsonata/parser.py,sha256=dzZ6PeM5l3scTHq4DzCXt4FSRO49yzYEk7XqspCcHNU,55359
8
+ jsonata/signature.py,sha256=j7eNKUuGx_9vCt5Qv8BPM7iV5vH26U0Kx8zrkDcctME,20194
9
+ jsonata/timebox.py,sha256=bnevNR_ONvKUiIZCJZEWsRiR0gCWTGOwn5RCY7dKqYc,2861
10
+ jsonata/tokenizer.py,sha256=6noGxO1L_n2V-Uva5oV044Xx0p08Gm7-XXSfLEG4rR0,12429
11
+ jsonata/utils.py,sha256=WDs-z5JRJ5pUEpmAwUY3xgDFxEIsvTqZXp8UX6cNSpQ,5952
12
+ jsonata/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ jsonata/cli/__main__.py,sha256=x1cz-w1NYBb_UNJuDnwqmGaz1SdfHwpMjKXIz5GQG-A,7121
14
+ jsonata_python-0.6.1.dist-info/METADATA,sha256=lNCEnZXUk7pzV8kP9unZq0AVkNoB2ZngEPbdfiaoUO8,17337
15
+ jsonata_python-0.6.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ jsonata_python-0.6.1.dist-info/entry_points.txt,sha256=a-W2yyT3IPhcC1q12BhKPcWnjNi3NXTZ3ZxoI9_UK88,54
17
+ jsonata_python-0.6.1.dist-info/licenses/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
18
+ jsonata_python-0.6.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jsonata = jsonata.cli.__main__:main
@@ -1,17 +0,0 @@
1
- jsonata/__init__.py,sha256=4r8USHj4SoBy_TD8dLxt9HJIgpLfXApFrZBipi4rgr0,388
2
- jsonata/constants.py,sha256=WtdH_l_s5KD-SiOJ4GR2az7WpVgKB2HguXUnyfy4tvs,3017
3
- jsonata/datetimeutils.py,sha256=IzU6y-vwyhwobHyw9rsImBye-RfWDV7K8Vk0Xs-yiG0,48567
4
- jsonata/functions.py,sha256=giAtWZ92cOlnBcH7Zkapnib-LPSPyBFHrz-9AMdkhqQ,75500
5
- jsonata/jexception.py,sha256=6Jz7WMsIiNlQ7-1Hq8RKiE2HxcHq2PDekw0qsSe3lqo,12885
6
- jsonata/jsonata.py,sha256=jZIxVrNcZI3aBvE0PURZ4-cxpaK9JiJVH5Q_cllCXS0,84247
7
- jsonata/parser.py,sha256=U8nNxvsDIy0wVTfNCs2DNN7RjNVm0SqfociD9cRcsWM,53877
8
- jsonata/signature.py,sha256=j7eNKUuGx_9vCt5Qv8BPM7iV5vH26U0Kx8zrkDcctME,20194
9
- jsonata/timebox.py,sha256=bnevNR_ONvKUiIZCJZEWsRiR0gCWTGOwn5RCY7dKqYc,2861
10
- jsonata/tokenizer.py,sha256=X-JsARtkBIN6bO8_TElpw3SCageQMtfBWlexuhG2Sds,11982
11
- jsonata/utils.py,sha256=U13I49Ie3hEn3PKGR4361TPengDAkbyHmwipDFZJlXo,5192
12
- jsonata/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- jsonata/cli/__main__.py,sha256=d_tNQ3Y_yN9o_NzBip8LdhsAVLAAA62GeowFKc6zx3Y,7125
14
- jsonata_python-0.5.3.dist-info/METADATA,sha256=_qkeeFX_iFWyljejtPGrx3Xjt8mgkX0tKIkTk_-uNlc,17442
15
- jsonata_python-0.5.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- jsonata_python-0.5.3.dist-info/licenses/LICENSE,sha256=y16Ofl9KOYjhBjwULGDcLfdWBfTEZRXnduOspt-XbhQ,11325
17
- jsonata_python-0.5.3.dist-info/RECORD,,