optimism 3.1.3__tar.gz → 3.1.4__tar.gz

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.
Files changed (38) hide show
  1. {optimism-3.1.3 → optimism-3.1.4}/PKG-INFO +3 -2
  2. {optimism-3.1.3 → optimism-3.1.4}/optimism/__init__.py +1 -1
  3. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/code.py +16 -0
  4. {optimism-3.1.3 → optimism-3.1.4}/optimism/optimism.py +34 -11
  5. {optimism-3.1.3 → optimism-3.1.4}/optimism/test_examples.py +82 -59
  6. {optimism-3.1.3 → optimism-3.1.4}/optimism.egg-info/PKG-INFO +3 -2
  7. {optimism-3.1.3 → optimism-3.1.4}/LICENSE.txt +0 -0
  8. {optimism-3.1.3 → optimism-3.1.4}/README.md +0 -0
  9. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/a.txt +0 -0
  10. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/b.txt +0 -0
  11. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/bad.py +0 -0
  12. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/basic.py +0 -0
  13. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/bits.py +0 -0
  14. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/block.py +0 -0
  15. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/catch_and_release.py +0 -0
  16. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/core.py +0 -0
  17. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/doc_example.py +0 -0
  18. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/doctests.py +0 -0
  19. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/errors.py +0 -0
  20. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/files.py +0 -0
  21. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/fragments.py +0 -0
  22. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/io_example.py +0 -0
  23. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/literals.py +0 -0
  24. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/memoryReports.py +0 -0
  25. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/multi.py +0 -0
  26. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/reverse_tracing.py +0 -0
  27. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/runfile.py +0 -0
  28. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/skip.py +0 -0
  29. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/structure.py +0 -0
  30. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/suites.py +0 -0
  31. {optimism-3.1.3 → optimism-3.1.4}/optimism/examples/tracing.py +0 -0
  32. {optimism-3.1.3 → optimism-3.1.4}/optimism/py.typed +0 -0
  33. {optimism-3.1.3 → optimism-3.1.4}/optimism.egg-info/SOURCES.txt +0 -0
  34. {optimism-3.1.3 → optimism-3.1.4}/optimism.egg-info/dependency_links.txt +0 -0
  35. {optimism-3.1.3 → optimism-3.1.4}/optimism.egg-info/requires.txt +0 -0
  36. {optimism-3.1.3 → optimism-3.1.4}/optimism.egg-info/top_level.txt +0 -0
  37. {optimism-3.1.3 → optimism-3.1.4}/pyproject.toml +0 -0
  38. {optimism-3.1.3 → optimism-3.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: optimism
3
- Version: 3.1.3
3
+ Version: 3.1.4
4
4
  Summary: Very simple testing framework suitable for beginner programmers, with easy functions for accomplishing most common testing tasks like providing input. Requires Python 3.8+.
5
5
  Home-page: https://cs.wellesley.edu/~pmwh/optimism/docs/optimism.html
6
6
  Author: Peter Mawhorter
@@ -19,6 +19,7 @@ Description-Content-Type: text/markdown
19
19
  License-File: LICENSE.txt
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
+ Dynamic: license-file
22
23
 
23
24
  # `optimism`
24
25
 
@@ -13,7 +13,7 @@ from .optimism import *
13
13
  # version from optimism, but we want the version to be defined in that
14
14
  # file so that a single-file distribution is equivalent to this packaged
15
15
  # distribution. So we define a version here as well, but...
16
- __version__ = "3.1.3"
16
+ __version__ = "3.1.4"
17
17
 
18
18
  # ...then we assert it matches so testing will catch it if I forget to
19
19
  # keep them in sync before a release. T_T
@@ -94,3 +94,19 @@ fileTester.checkCodeContains(optimism.Constant(4.0 / 2, float)) # ✗
94
94
  fileTester.checkCodeContains(optimism.Constant(4.0 / 2, int)) # ✓
95
95
  fileTester.checkCodeContains(optimism.Constant(5.0, int)) # ✗
96
96
  fileTester.checkCodeContains(optimism.Constant(5.0, float)) # ✓
97
+
98
+ # Test augmented assignment matching
99
+ def aug():
100
+ x = 3
101
+ x += 4
102
+ x /= 10
103
+
104
+ fileTester.checkCodeContains(
105
+ optimism.Def().contains(optimism.Operator('+'))
106
+ ) # ✓
107
+ fileTester.checkCodeContains(
108
+ optimism.Def().contains(optimism.Operator('/'))
109
+ ) # ✓
110
+ fileTester.checkCodeContains(
111
+ optimism.Def(name="aug").contains(optimism.Operator('/', max=0))
112
+ ) # ✗
@@ -200,6 +200,11 @@ TODO: Workaround for tracing in interactive console?
200
200
 
201
201
  ## Changelog
202
202
 
203
+ - Version 3.1.4 treats augmented assignments as operators for
204
+ `checkCodeContains` purposes, as they should have been all along. It
205
+ adds a new test for that in the `code.py` example file, and also fixes
206
+ up some modern mypy type warnings (even though they were mostly
207
+ spurious).
203
208
  - Version 3.1.3 Fixes a numbering bug with memory reports and also fixes
204
209
  a pernicious `deepish_copy` bug: Structures that were supposed to be
205
210
  duplicated would have their top levels duplicated twice separately
@@ -353,7 +358,7 @@ TODO: Workaround for tracing in interactive console?
353
358
 
354
359
  # TODO: Cache compiled ASTs!
355
360
 
356
- __version__ = "3.1.3"
361
+ __version__ = "3.1.4"
357
362
 
358
363
  from typing import (
359
364
  Any, Dict, List, Optional, Tuple, Literal as LiteralType, Callable,
@@ -378,7 +383,7 @@ SPLIT_CONSTANTS = hasattr(ast, "Num")
378
383
 
379
384
  # Type is required before 3.9 but deprecated in that version
380
385
  if sys.version_info >= (3, 9):
381
- Type = type
386
+ from builtins import type as Type
382
387
  else:
383
388
  from typing import Type
384
389
 
@@ -397,19 +402,19 @@ except ImportError:
397
402
  def __getitem__(self, _):
398
403
  return Any
399
404
 
400
- Unpack = UnpackShim()
405
+ Unpack = UnpackShim() # type: ignore
401
406
 
402
407
  # Versions of Python before 3.10 don't support TypeAlias
403
408
  try:
404
409
  from typing import TypeAlias
405
410
  except ImportError:
406
- TypeAlias = Type
411
+ TypeAlias = Type # type: ignore
407
412
 
408
413
  # Versions of Python before 3.10 don't support NoneType
409
414
  try:
410
415
  from types import NoneType
411
416
  except ImportError:
412
- NoneType = type(None)
417
+ NoneType = type(None) # type: ignore
413
418
 
414
419
  #----------------#
415
420
  # Monkey Patches #
@@ -730,7 +735,11 @@ Type variable for `ClassInfo` definition.
730
735
 
731
736
 
732
737
  if sys.version_info > (3, 10):
733
- ClassInfo: TypeAlias = Union[Type[T], Tuple['ClassInfo', ...], Type[Union]]
738
+ ClassInfo: TypeAlias = Union[
739
+ Type[T],
740
+ Tuple['ClassInfo', ...],
741
+ Type[Union]
742
+ ]
734
743
  else:
735
744
  ClassInfo: TypeAlias = Union[Type[T], Tuple['ClassInfo', ...]]
736
745
  """
@@ -3913,7 +3922,7 @@ class QuietSkipManager(TestManager[SilentCase]):
3913
3922
  # Test factories #
3914
3923
  #----------------#
3915
3924
 
3916
- def testFunction(fn: FunctionType) -> FunctionManager:
3925
+ def testFunction(fn: Union[FunctionType, Callable]) -> FunctionManager:
3917
3926
  """
3918
3927
  Creates a test-manager for the given function.
3919
3928
  """
@@ -6005,7 +6014,9 @@ class Loop(ASTRequirement):
6005
6014
  """
6006
6015
  def __init__(
6007
6016
  self,
6008
- only: Optional[Iterable[ExtendedLoopType]] = None,
6017
+ only: Optional[
6018
+ Union[ExtendedLoopType, Iterable[ExtendedLoopType]]
6019
+ ] = None,
6009
6020
  **kwargs: Unpack[NumArgs]
6010
6021
  ) -> None:
6011
6022
  """
@@ -6867,7 +6878,13 @@ class Operator(ASTRequirement):
6867
6878
  ) -> Iterator[Tuple[ast.AST, bool]]:
6868
6879
  for node in self._walkNodesOfType(
6869
6880
  syntax_tree,
6870
- (ast.UnaryOp, ast.BinOp, ast.BoolOp, ast.Compare)
6881
+ (
6882
+ ast.UnaryOp,
6883
+ ast.BinOp,
6884
+ ast.BoolOp,
6885
+ ast.Compare,
6886
+ ast.AugAssign
6887
+ )
6871
6888
  ):
6872
6889
  # Determine not/partial/full status of match...
6873
6890
  match: LiteralType[True, "partial", False] = False
@@ -7146,7 +7163,7 @@ which has slots that used to contain complex sub-objects replaced with
7146
7163
  `MemReference` objects. See `memoryMap`.
7147
7164
  """
7148
7165
 
7149
- SmallTypes: Tuple[Type] = (int, float, complex, bool, NoneType)
7166
+ SmallTypes: Tuple[Type, ...] = (int, float, complex, bool, NoneType)
7150
7167
  """
7151
7168
  The object types which are small enough to fit into a single field
7152
7169
  without using a pointer in theory, which we thus represent without using
@@ -7157,7 +7174,7 @@ with `isinstance`.
7157
7174
  # later so it cannot just be None. If we only wanted to support Python
7158
7175
  # 3.10+, we could use the union directly in isinstance.
7159
7176
 
7160
- SmallType: TypeAlias = Union[int, float, complex, bool, NoneType]
7177
+ SmallType: TypeAlias = Union[int, float, complex, bool, None]
7161
7178
  """
7162
7179
  A union of the `SmallTypes`.
7163
7180
  """
@@ -7231,6 +7248,7 @@ def memoryMap(
7231
7248
  or (INLINE_STRINGS_IN_MEMORY_REPORTS and isinstance(obj, str))
7232
7249
  ):
7233
7250
  # Simple values are used as-is:
7251
+ obj = cast(SmallType, obj)
7234
7252
  return None, obj
7235
7253
  elif isinstance(obj, (tuple, list, set)):
7236
7254
  # Structures are made shallow and referenced
@@ -9881,6 +9899,11 @@ def get_ref_src(src: str, node: ast.AST) -> str:
9881
9899
 
9882
9900
  # We're going to have to do this ourself: find the start of the
9883
9901
  # expression and state-machine to find its end
9902
+ if not hasattr(node, "lineno") or not hasattr(node, "col_offset"):
9903
+ raise TypeError(
9904
+ f"Node {node!r} is missing lineno and/or col_offset"
9905
+ f" attribute(s)."
9906
+ )
9884
9907
  start = get_src_index(src, node.lineno, node.col_offset)
9885
9908
 
9886
9909
  # Figure out the end point
@@ -518,8 +518,9 @@ All 4 expectation(s) were met.
518
518
  Code does not contain the expected structure:
519
519
  at least 1 definition(s) of functionName
520
520
  Although it does partially satisfy the requirement:
521
- Requirement partially satisfied via 0 full and 1 partial match(es):
521
+ Requirement partially satisfied via 0 full and 2 partial match(es):
522
522
  Partial match: FunctionDef on line 10
523
+ Partial match: FunctionDef on line 99
523
524
  checked code in file '{full_file}'""",
524
525
  "✓ {file}:44",
525
526
  """\
@@ -528,8 +529,9 @@ All 4 expectation(s) were met.
528
529
  at least 1 function definition(s) that also contain(s):
529
530
  at least 1 loop(s) or generator expression(s)
530
531
  Although it does partially satisfy the requirement:
531
- Requirement partially satisfied via 0 full and 1 partial match(es):
532
+ Requirement partially satisfied via 0 full and 2 partial match(es):
532
533
  Partial match: FunctionDef on line 10
534
+ Partial match: FunctionDef on line 99
533
535
  checked code in file '{full_file}'""",
534
536
  """\
535
537
  ✗ {file}:46
@@ -601,24 +603,36 @@ All 4 expectation(s) were met.
601
603
  Code does not contain the expected structure:
602
604
  at least 1 constant 2.0
603
605
  Although it does partially satisfy the requirement:
604
- Requirement partially satisfied via 0 full and 52 partial match(es):
606
+ Requirement partially satisfied via 0 full and 60 partial match(es):
605
607
  """ + ("""\
606
608
  Partial match: Constant on line 1
607
609
  Partial match: Constant on line 7""" if better_line_numbers else """\
608
610
  Partial match: Str on line 3
609
- Partial match: NameConstant on line 7"""), # not listing all 52...
611
+ Partial match: NameConstant on line 7"""), # not listing all 60...
610
612
  "✓ {file}:94",
611
613
  """\
612
614
  ✗ {file}:95
613
615
  Code does not contain the expected structure:
614
616
  at least 1 constant 5.0
615
617
  Although it does partially satisfy the requirement:
616
- Requirement partially satisfied via 0 full and 52 partial match(es):
618
+ Requirement partially satisfied via 0 full and 60 partial match(es):
617
619
  """ + ("""\
618
620
  Partial match: Constant on line 1
619
621
  Partial match: Constant on line 7""" if better_line_numbers else """\
620
622
  Partial match: Str on line 3
621
- Partial match: NameConstant on line 7"""), # not listing all 52...
623
+ Partial match: NameConstant on line 7"""), # not listing all 60...
624
+ "✓ {file}:104",
625
+ "✓ {file}:107",
626
+ """\
627
+ ✗ {file}:110
628
+ Code does not contain the expected structure:
629
+ at least 1 definition(s) of aug that also contain(s):
630
+ no operator '/'
631
+ Although it does partially satisfy the requirement:
632
+ Requirement partially satisfied via 0 full and 2 partial match(es):
633
+ Partial match: FunctionDef on line 99
634
+ Partial match: FunctionDef on line 10
635
+ """
622
636
  ],
623
637
 
624
638
  "reverse_tracing": [
@@ -1360,63 +1374,72 @@ def test_all():
1360
1374
  Tests each example file. The `examples` directory must be in the
1361
1375
  same place that this `test_examples.py` file lives.
1362
1376
  """
1363
- os.chdir(os.path.join(os.path.dirname(__file__), "examples"))
1364
- sys.path.insert(0, '.')
1365
- for (mname, inputs) in EXAMPLES:
1366
- optimism.detailLevel(0)
1367
- optimism.deleteAllTestSuites()
1368
- optimism.colors(False)
1369
- module, output, stdout = run_example(mname, inputs)
1370
- # Replace tracebacks with generic forms so we can match on
1371
- # specific errors being reported while ignoring the files & lines
1372
- # where they are reported.
1373
- stdout = re.sub(
1374
- r"Traceback \(most recent call last\):.*?^([ a-zA-Z]*Error:)",
1375
- "Traceback omitted\n\\1",
1376
- stdout,
1377
- flags=re.MULTILINE | re.DOTALL
1378
- )
1379
- output = re.sub(
1380
- r"Traceback \(most recent call last\):.*?^([ a-zA-Z]*Error:)",
1381
- "Traceback omitted\n\\1",
1382
- output,
1383
- flags=re.MULTILINE | re.DOTALL
1384
- )
1385
- #output = re.sub(r"Traceback \(most recent call last\).*?NameError:", "HAHAHA", output, flags=re.MULTILINE | re.DOTALL)
1386
- # Scrub line numbers from tracebacks so our expectations don't
1387
- # become hopelessly fragile
1388
- stdout = re.sub(r", line \d+,", ", line X,", stdout)
1389
- output = re.sub(r", line \d+,", ", line X,", output)
1390
- for ex in EXPECTATIONS[mname]:
1391
- exp = ex.format(
1392
- file=os.path.basename(module.__file__),
1393
- dir=(
1394
- os.path.split(module.__file__)[0] + '/'
1395
- if better_line_numbers
1396
- else ''
1397
- ),
1398
- full_file=module.__file__,
1399
- opt_file=optimism.__file__,
1377
+ olddir = os.getcwd()
1378
+ try:
1379
+ os.chdir(os.path.join(os.path.dirname(__file__), "examples"))
1380
+ sys.path.insert(0, '.')
1381
+ for (mname, inputs) in EXAMPLES:
1382
+ optimism.detailLevel(0)
1383
+ optimism.deleteAllTestSuites()
1384
+ optimism.colors(False)
1385
+ module, output, stdout = run_example(mname, inputs)
1386
+ # Replace tracebacks with generic forms so we can match on
1387
+ # specific errors being reported while ignoring the files & lines
1388
+ # where they are reported.
1389
+ stdout = re.sub(
1390
+ r"Traceback \(most recent call last\):.*?^([ a-zA-Z]*Error:)",
1391
+ "Traceback omitted\n\\1",
1392
+ stdout,
1393
+ flags=re.MULTILINE | re.DOTALL
1400
1394
  )
1401
- # Redundant, but makes issues easier to diagnose
1402
- for line in exp.splitlines():
1403
- if line not in output:
1404
- print("\n\nLINE:\n" + line)
1405
- print("\n\nOUTPUT:\n" + output)
1406
- assert (line in output), (line, output)
1407
- if exp not in output:
1408
- print("\n\nEXP:\n" + exp)
1409
- print("\n\nOUTPUT:\n" + output)
1410
- assert (exp in output), (exp, output)
1411
- if mname in STDOUT_EXPECT:
1412
- for ex in STDOUT_EXPECT[mname]:
1395
+ output = re.sub(
1396
+ r"Traceback \(most recent call last\):.*?^([ a-zA-Z]*Error:)",
1397
+ "Traceback omitted\n\\1",
1398
+ output,
1399
+ flags=re.MULTILINE | re.DOTALL
1400
+ )
1401
+ #output = re.sub(r"Traceback \(most recent call last\).*?NameError:", "HAHAHA", output, flags=re.MULTILINE | re.DOTALL)
1402
+ # Scrub line numbers from tracebacks so our expectations don't
1403
+ # become hopelessly fragile
1404
+ stdout = re.sub(r", line \d+,", ", line X,", stdout)
1405
+ output = re.sub(r", line \d+,", ", line X,", output)
1406
+ for ex in EXPECTATIONS[mname]:
1413
1407
  exp = ex.format(
1414
1408
  file=os.path.basename(module.__file__),
1415
- dir=os.path.split(module.__file__)[0],
1409
+ dir=(
1410
+ os.path.split(module.__file__)[0] + '/'
1411
+ if better_line_numbers
1412
+ else ''
1413
+ ),
1416
1414
  full_file=module.__file__,
1417
1415
  opt_file=optimism.__file__,
1418
1416
  )
1417
+ # Redundant, but makes issues easier to diagnose
1419
1418
  for line in exp.splitlines():
1420
- assert (line in stdout), stdout
1421
- assert (exp in stdout), stdout
1422
- os.chdir("..")
1419
+ if line not in output:
1420
+ print("\n\nLINE:\n" + line)
1421
+ print("\n\nOUTPUT:\n" + output)
1422
+ assert (line in output), (line, output)
1423
+ if exp not in output:
1424
+ print("\n\nEXP:\n" + exp)
1425
+ print("\n\nOUTPUT:\n" + output)
1426
+ assert (exp in output), (exp, output)
1427
+ if mname in STDOUT_EXPECT:
1428
+ for ex in STDOUT_EXPECT[mname]:
1429
+ exp = ex.format(
1430
+ file=os.path.basename(module.__file__),
1431
+ dir=os.path.split(module.__file__)[0],
1432
+ full_file=module.__file__,
1433
+ opt_file=optimism.__file__,
1434
+ )
1435
+ for line in exp.splitlines():
1436
+ assert (line in stdout), stdout
1437
+ assert (exp in stdout), stdout
1438
+ finally:
1439
+ os.chdir(olddir)
1440
+
1441
+
1442
+ if __name__ == "__main__":
1443
+ print('Testing example files...')
1444
+ test_all()
1445
+ print('...all tests passed.')
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: optimism
3
- Version: 3.1.3
3
+ Version: 3.1.4
4
4
  Summary: Very simple testing framework suitable for beginner programmers, with easy functions for accomplishing most common testing tasks like providing input. Requires Python 3.8+.
5
5
  Home-page: https://cs.wellesley.edu/~pmwh/optimism/docs/optimism.html
6
6
  Author: Peter Mawhorter
@@ -19,6 +19,7 @@ Description-Content-Type: text/markdown
19
19
  License-File: LICENSE.txt
20
20
  Provides-Extra: test
21
21
  Requires-Dist: pytest; extra == "test"
22
+ Dynamic: license-file
22
23
 
23
24
  # `optimism`
24
25
 
File without changes
File without changes
File without changes
File without changes
File without changes