django-orm-lens 1.2.5__tar.gz → 1.2.6__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.
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/PKG-INFO +1 -1
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/parser.py +6 -1
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/PKG-INFO +1 -1
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/SOURCES.txt +2 -1
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/pyproject.toml +1 -1
- django_orm_lens-1.2.6/tests/test_tab_indented_models.py +54 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/README.md +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/__init__.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/cli.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/diff.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/formatters.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/mcp_server.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/migrations_parser.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/models.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/py.typed +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/query_analyzer.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens/signals_parser.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/dependency_links.txt +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/entry_points.txt +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/requires.txt +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/top_level.txt +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/setup.cfg +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_abstract_and_auth_user.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_aliased_module_and_third_party_fields.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_cli_hint.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_cli_subcommands_smoke.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_cli_verbose.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_cross_module_bases.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_diff_command.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_golden_fixtures.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_kwarg_order_and_auth_user.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_list_command.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_migration_risk.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_migrations_parser.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_nplusone_detector.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_parity.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_pep526_type_hints.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_pep695_generic_classes.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_query_analyzer.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_scan_root.py +0 -0
- {django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_signals_parser.py +0 -0
|
@@ -398,7 +398,12 @@ def _collect_defs(file_path: str, content: str) -> List[ParsedModel]:
|
|
|
398
398
|
result with definitions from other files before calling
|
|
399
399
|
``_resolve_and_filter``.
|
|
400
400
|
"""
|
|
401
|
-
|
|
401
|
+
# Expand tab characters to 4 spaces so the ``\s{indent}`` prefix used in
|
|
402
|
+
# FIELD_RE / BARE_FIELD_RE / META_ITEM_RE matches tab-indented code too.
|
|
403
|
+
# A single ``\t`` character otherwise contributes 1 to ``\s`` even though
|
|
404
|
+
# it's visually 4 columns — tab-indented models used to have zero fields
|
|
405
|
+
# detected. Line numbers are preserved (per-line expansion only).
|
|
406
|
+
lines = [line.expandtabs(4) for line in content.splitlines()]
|
|
402
407
|
all_defs: List[ParsedModel] = []
|
|
403
408
|
parent = os.path.basename(os.path.dirname(file_path))
|
|
404
409
|
if parent == "models":
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-orm-lens"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.6"
|
|
8
8
|
description = "Static analysis + MCP server for Django models. Sidebar tree, ER diagrams, and JSON output for terminals and AI coding agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Regression: tab-indented model bodies must parse.
|
|
2
|
+
|
|
3
|
+
Editors that default to tabs (or projects that use PEP-8 exceptions with
|
|
4
|
+
tabs) produced empty models - every field silently dropped. Root cause:
|
|
5
|
+
``FIELD_RE`` uses ``\\s{indent}`` as its column prefix and
|
|
6
|
+
``_detect_class_indent`` expands tabs to width 4, but a single ``\\t``
|
|
7
|
+
character is only one match for ``\\s``, not four. Fixed by pre-expanding
|
|
8
|
+
tabs to spaces in the line buffer before regex matching.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import unittest
|
|
14
|
+
|
|
15
|
+
from django_orm_lens.parser import parse_models_file
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TabIndentationTest(unittest.TestCase):
|
|
19
|
+
def test_pure_tab_indented_body(self) -> None:
|
|
20
|
+
src = (
|
|
21
|
+
"class Post(models.Model):\n"
|
|
22
|
+
"\ttitle = models.CharField(max_length=100)\n"
|
|
23
|
+
"\tauthor = models.CharField(max_length=100)\n"
|
|
24
|
+
"\tbody = models.TextField()\n"
|
|
25
|
+
)
|
|
26
|
+
models = parse_models_file("blog/models.py", src)
|
|
27
|
+
self.assertEqual(len(models), 1)
|
|
28
|
+
self.assertEqual(
|
|
29
|
+
[f.name for f in models[0].fields],
|
|
30
|
+
["title", "author", "body"],
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def test_mixed_tab_then_space(self) -> None:
|
|
34
|
+
# Editors that mid-migrate to tabs sometimes end up with a tab
|
|
35
|
+
# followed by a space; that used to fail equally silently.
|
|
36
|
+
src = (
|
|
37
|
+
"class M(models.Model):\n"
|
|
38
|
+
"\t x = models.CharField(max_length=5)\n"
|
|
39
|
+
)
|
|
40
|
+
models = parse_models_file("t/models.py", src)
|
|
41
|
+
self.assertEqual([f.name for f in models[0].fields], ["x"])
|
|
42
|
+
|
|
43
|
+
def test_space_indented_still_works(self) -> None:
|
|
44
|
+
# Backward-compat guard.
|
|
45
|
+
src = (
|
|
46
|
+
"class M(models.Model):\n"
|
|
47
|
+
" x = models.CharField(max_length=5)\n"
|
|
48
|
+
)
|
|
49
|
+
models = parse_models_file("t/models.py", src)
|
|
50
|
+
self.assertEqual([f.name for f in models[0].fields], ["x"])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/django_orm_lens.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_orm_lens-1.2.5 → django_orm_lens-1.2.6}/tests/test_aliased_module_and_third_party_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|