Skip to content

Commit

Permalink
Merge pull request #195 from 15r10nk/bugfixing
Browse files Browse the repository at this point in the history
Bugfixing
  • Loading branch information
15r10nk authored Feb 12, 2025
2 parents 7b065a4 + 1aec8ef commit 18aafeb
Show file tree
Hide file tree
Showing 27 changed files with 475 additions and 190 deletions.
3 changes: 3 additions & 0 deletions changelog.d/20250202_081752_15r10nk-git_new_defaults.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Fixed

- snapshots inside tests which are marked as xfail are now ignored (#184)
9 changes: 9 additions & 0 deletions changelog.d/20250211_230349_15r10nk-git_bugfixing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### Fixed

- Fixed a crash caused by the following code:

``` python
snapshot(tuple())
# or
snapshot(dict())
```
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ serve = "mkdocs serve"
dependencies=["cogapp","lxml","requests"]
scripts.update="cog -r docs/**.md"

[tool.hatch.envs.gen]
dependencies=["pysource-minimize"]
scripts.test=["python testing/generate_tests.py"]

[[tool.hatch.envs.hatch-test.matrix]]
python = ["3.13", "3.12", "3.11", "3.10", "3.9", "3.8","pypy3.9","pypy3.10"]
extra-deps=["low","hight"]
Expand Down
2 changes: 2 additions & 0 deletions src/inline_snapshot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ._code_repr import HasRepr
from ._code_repr import customize_repr
from ._exceptions import UsageError
from ._external import external
from ._external import outsource
from ._inline_snapshot import snapshot
Expand All @@ -16,6 +17,7 @@
"Is",
"Category",
"Snapshot",
"UsageError",
]

__version__ = "0.20.1"
4 changes: 1 addition & 3 deletions src/inline_snapshot/_adapter/dict_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ def map(cls, value, map_function):

@classmethod
def items(cls, value, node):
if node is None:
if node is None or not isinstance(node, ast.Dict):
return [Item(value=value, node=None) for value in value.values()]

assert isinstance(node, ast.Dict)

result = []

for value_key, node_key, node_value in zip(
Expand Down
3 changes: 1 addition & 2 deletions src/inline_snapshot/_adapter/sequence_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ def map(cls, value, map_function):

@classmethod
def items(cls, value, node):
if node is None:
if node is None or not isinstance(node, cls.node_type):
return [Item(value=v, node=None) for v in value]

assert isinstance(node, cls.node_type), (node, cls)
assert len(value) == len(node.elts)

return [Item(value=v, node=n) for v, n in zip(value, node.elts)]
Expand Down
16 changes: 10 additions & 6 deletions src/inline_snapshot/_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Change:
def filename(self):
return self.file.filename

def apply(self):
def apply(self, recorder: ChangeRecorder):
raise NotImplementedError()


Expand Down Expand Up @@ -76,8 +76,8 @@ class Replace(Change):
old_value: Any
new_value: Any

def apply(self):
change = ChangeRecorder.current.new_change()
def apply(self, recorder: ChangeRecorder):
change = recorder.new_change()
range = self.file.asttokens().get_text_positions(self.node, False)
change.replace(range, self.new_code, filename=self.filename)

Expand Down Expand Up @@ -106,8 +106,9 @@ def generic_sequence_update(
brace_tokens: TokenRange,
parent_elements: List[Union[TokenRange, None]],
to_insert: Dict[int, List[str]],
recorder: ChangeRecorder,
):
rec = ChangeRecorder.current.new_change()
rec = recorder.new_change()

new_code = []
deleted = False
Expand Down Expand Up @@ -162,7 +163,7 @@ def generic_sequence_update(
)


def apply_all(all_changes: List[Change]):
def apply_all(all_changes: List[Change], recorder: ChangeRecorder):
by_parent: Dict[
EnhancedAST, List[Union[Delete, DictInsert, ListInsert, CallArg]]
] = defaultdict(list)
Expand All @@ -181,7 +182,7 @@ def apply_all(all_changes: List[Change]):
by_parent[node].append(change)
sources[node] = change.file
else:
change.apply()
change.apply(recorder)

for parent, changes in by_parent.items():
source = sources[parent]
Expand All @@ -206,6 +207,7 @@ def list_token_range(entry):
brace_tokens(source, parent),
[None if e in to_delete else list_token_range(e) for e in parent.elts],
to_insert,
recorder,
)

elif isinstance(parent, ast.Call):
Expand Down Expand Up @@ -251,6 +253,7 @@ def arg_token_range(node):
for e in parent.args + [kw.value for kw in parent.keywords]
],
to_insert,
recorder,
)

elif isinstance(parent, ast.Dict):
Expand Down Expand Up @@ -278,6 +281,7 @@ def dict_token_range(key, value):
for key, value in zip(parent.keys, parent.values)
],
to_insert,
recorder,
)

else:
Expand Down
10 changes: 5 additions & 5 deletions src/inline_snapshot/_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Union

from . import _config
from ._global_state import state


class HashError(Exception):
Expand Down Expand Up @@ -70,9 +71,6 @@ def remove(self, name):
self._lookup_path(name).unlink()


storage: Optional[DiscStorage] = None


class external:
def __init__(self, name: str):
"""External objects are used as a representation for outsourced data.
Expand Down Expand Up @@ -130,8 +128,8 @@ def __eq__(self, other):
return True

def _load_value(self):
assert storage is not None
return storage.read(self._path)
assert state().storage is not None
return state().storage.read(self._path)


def outsource(data: Union[str, bytes], *, suffix: Optional[str] = None) -> external:
Expand Down Expand Up @@ -170,6 +168,8 @@ def outsource(data: Union[str, bytes], *, suffix: Optional[str] = None) -> exter
m.update(data)
hash = m.hexdigest()

storage = state().storage

assert storage is not None

name = hash + suffix
Expand Down
13 changes: 7 additions & 6 deletions src/inline_snapshot/_find_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from executing import Source

from . import _external
from ._global_state import state
from ._rewrite_code import ChangeRecorder
from ._rewrite_code import end_of
Expand Down Expand Up @@ -54,7 +53,7 @@ def used_externals() -> Set[str]:


def unused_externals() -> Set[str]:
storage = _external.storage
storage = state().storage
assert storage is not None
unused_externals = storage.list()
for name in used_externals():
Expand All @@ -63,10 +62,10 @@ def unused_externals() -> Set[str]:
return unused_externals


def ensure_import(filename, imports):
def ensure_import(filename, imports, recorder: ChangeRecorder):
source = Source.for_filename(filename)

change = ChangeRecorder.current.new_change()
change = recorder.new_change()

tree = source.tree
token = source.asttokens()
Expand All @@ -78,16 +77,18 @@ def ensure_import(filename, imports):
if not contains_import(tree, module, name):
to_add.append((module, name))

assert isinstance(tree, ast.Module)

last_import = None
for node in tree.body:
if not isinstance(node, (ast.ImportFrom, ast.Import)):
break
last_import = node

if last_import is None:
position = start_of(tree.body[0].first_token)
position = start_of(tree.body[0].first_token) # type: ignore
else:
last_token = last_import.last_token
last_token = last_import.last_token # type: ignore
while True:
next_token = token.next_token(last_token)
if last_token.end[0] == next_token.end[0]:
Expand Down
6 changes: 5 additions & 1 deletion src/inline_snapshot/_global_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
import contextlib
from dataclasses import dataclass
from dataclasses import field
from typing import TYPE_CHECKING
from typing import Generator

from ._flags import Flags

if TYPE_CHECKING:
from ._external import DiscStorage


@dataclass
class State:
Expand All @@ -20,7 +24,7 @@ class State:
files_with_snapshots: set[str] = field(default_factory=set)

# external
storage = None
storage: DiscStorage | None = None


_current = State()
Expand Down
16 changes: 0 additions & 16 deletions src/inline_snapshot/_rewrite_code.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import contextlib
import logging
import pathlib
import sys
Expand Down Expand Up @@ -199,29 +198,18 @@ def diff(self):


class ChangeRecorder:
current: ChangeRecorder

def __init__(self):
self._source_files = defaultdict(SourceFile)
self._changes = []

@contextlib.contextmanager
def activate(self):
old_recorder = ChangeRecorder.current
ChangeRecorder.current = self
yield self
ChangeRecorder.current = old_recorder

def get_source(self, filename) -> SourceFile:
filename = pathlib.Path(filename)
if filename not in self._source_files:
self._source_files[filename] = SourceFile(filename)

return self._source_files[filename]

def change_set(self):
return Change(self)

def files(self) -> Iterable[SourceFile]:
return self._source_files.values()

Expand All @@ -247,7 +235,3 @@ def dump(self): # pragma: no cover
print("file:", file.filename)
for change in file.replacements:
print(" change:", change)


global_recorder = ChangeRecorder()
ChangeRecorder.current = global_recorder
Loading

0 comments on commit 18aafeb

Please sign in to comment.