TypeDAL 4.4.1__tar.gz → 4.4.3__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.
- {typedal-4.4.1 → typedal-4.4.3}/.crush/crush.db-shm +0 -0
- typedal-4.4.3/.crush/crush.db-wal +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.crush/logs/crush.log +9 -0
- {typedal-4.4.1 → typedal-4.4.3}/CHANGELOG.md +12 -0
- {typedal-4.4.1 → typedal-4.4.3}/PKG-INFO +3 -2
- {typedal-4.4.1 → typedal-4.4.3}/mkdocs.yml +2 -1
- {typedal-4.4.1 → typedal-4.4.3}/pyproject.toml +2 -1
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/__about__.py +1 -1
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/caching.py +6 -21
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/config.py +2 -84
- typedal-4.4.3/tasks.py +54 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_relationships.py +1 -1
- typedal-4.4.1/.crush/crush.db-wal +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.crush/.gitignore +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.crush/init +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.github/workflows/su6.yml +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.gitignore +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/.readthedocs.yml +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/README.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/coverage.svg +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/1_getting_started.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/2_defining_tables.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/3_building_queries.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/4_relationships.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/5_py4web.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/6_migrations.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/7_configuration.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/8_mixins.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/9_memoization.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/css/code_blocks.css +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/index.md +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/docs/requirements.txt +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/example_new.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/example_old.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/__init__.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/cli.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/constants.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/core.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/define.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/fields.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/for_py4web.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/for_web2py.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/helpers.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/mixins.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/py.typed +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/query_builder.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/relationships.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/rows.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/serializers/as_json.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/tables.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/types.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/src/typedal/web2py_py4web_shared.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/__init__.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/configs/simple.toml +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/configs/valid.env +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/configs/valid.toml +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/py314_tests.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_cli.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_config.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_docs_examples.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_helpers.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_json.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_main.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_mixins.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_mypy.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_orm.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_py4web.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_query_builder.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_row.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_stats.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_table.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_web2py.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/test_xx_others.py +0 -0
- {typedal-4.4.1 → typedal-4.4.3}/tests/timings.py +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -23,3 +23,12 @@
|
|
|
23
23
|
{"time":"2026-01-26T17:11:34.786142525+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).createAndStartLSPClient","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":76},"msg":"LSP client initialized","name":"python"}
|
|
24
24
|
{"time":"2026-01-26T17:11:43.042930337+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
25
25
|
{"time":"2026-01-26T17:11:44.911909276+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools.init.func1","file":"github.com/charmbracelet/crush/internal/agent/tools/rg.go","line":18},"msg":"Ripgrep (rg) not found in $PATH. Some grep features might be limited or slower."}
|
|
26
|
+
{"time":"2026-01-26T18:10:19.727802782+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":55},"msg":"Fetching providers from Catwalk"}
|
|
27
|
+
{"time":"2026-01-26T18:10:19.926137244+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/config.(*catwalkSync).Get.func1","file":"github.com/charmbracelet/crush/internal/config/catwalk.go","line":63},"msg":"Catwalk providers not modified"}
|
|
28
|
+
{"time":"2026-01-26T18:10:19.926245207+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/config.(*Config).configureProviders","file":"github.com/charmbracelet/crush/internal/config/load.go","line":259},"msg":"Skipping provider due to missing API key","provider":"anthropic"}
|
|
29
|
+
{"time":"2026-01-26T18:10:19.927808467+01:00","level":"INFO","msg":"goose: no migrations to run. current version: 20250812000000"}
|
|
30
|
+
{"time":"2026-01-26T18:10:19.927858435+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).initLSPClients","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":21},"msg":"LSP clients initialization started in background"}
|
|
31
|
+
{"time":"2026-01-26T18:10:19.928006013+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.New.func1","file":"github.com/charmbracelet/crush/internal/app/app.go","line":110},"msg":"Initializing MCP clients"}
|
|
32
|
+
{"time":"2026-01-26T18:10:20.546676356+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/app.(*App).createAndStartLSPClient","file":"github.com/charmbracelet/crush/internal/app/lsp.go","line":76},"msg":"LSP client initialized","name":"python"}
|
|
33
|
+
{"time":"2026-01-26T18:11:07.640056766+01:00","level":"INFO","source":{"function":"github.com/charmbracelet/crush/internal/agent.(*sessionAgent).generateTitle","file":"github.com/charmbracelet/crush/internal/agent/agent.go","line":788},"msg":"generated title with small model"}
|
|
34
|
+
{"time":"2026-01-26T18:11:11.201161834+01:00","level":"WARN","source":{"function":"github.com/charmbracelet/crush/internal/agent/tools.init.func1","file":"github.com/charmbracelet/crush/internal/agent/tools/rg.go","line":18},"msg":"Ripgrep (rg) not found in $PATH. Some grep features might be limited or slower."}
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
<!--next-version-placeholder-->
|
|
4
4
|
|
|
5
|
+
## v4.4.3 (2026-02-11)
|
|
6
|
+
|
|
7
|
+
### Fix
|
|
8
|
+
|
|
9
|
+
* Move env shell-style expansion to configuraptor, improved functionality using expandvars instead of custom implementation ([`8b41b91`](https://github.com/trialandsuccess/TypeDAL/commit/8b41b91d55c8893d5ce15c2decf6daa855fa230b))
|
|
10
|
+
|
|
11
|
+
## v4.4.2 (2026-01-26)
|
|
12
|
+
|
|
13
|
+
### Fix
|
|
14
|
+
|
|
15
|
+
* **memoize:** Simplify `related_tables` logic by just looking at the raw Rows.fields ([`3dee337`](https://github.com/trialandsuccess/TypeDAL/commit/3dee3379a2bbcd493be273344303aff0aff069a7))
|
|
16
|
+
|
|
5
17
|
## v4.4.1 (2026-01-26)
|
|
6
18
|
|
|
7
19
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TypeDAL
|
|
3
|
-
Version: 4.4.
|
|
3
|
+
Version: 4.4.3
|
|
4
4
|
Summary: Typing support for PyDAL
|
|
5
5
|
Project-URL: Documentation, https://typedal.readthedocs.io/
|
|
6
6
|
Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
16
16
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
17
17
|
Requires-Python: >=3.11
|
|
18
18
|
Requires-Dist: configurable-json<2
|
|
19
|
-
Requires-Dist: configuraptor<2
|
|
19
|
+
Requires-Dist: configuraptor<3,>=2.0.1
|
|
20
20
|
Requires-Dist: dill<1
|
|
21
21
|
Requires-Dist: legacy-cgi; python_version >= '3.13'
|
|
22
22
|
Requires-Dist: pydal>=20251012.3
|
|
@@ -32,6 +32,7 @@ Requires-Dist: tomlkit; extra == 'all'
|
|
|
32
32
|
Requires-Dist: typer<0.19,>=0.18; extra == 'all'
|
|
33
33
|
Provides-Extra: dev
|
|
34
34
|
Requires-Dist: contextlib-chdir; extra == 'dev'
|
|
35
|
+
Requires-Dist: ewok; extra == 'dev'
|
|
35
36
|
Requires-Dist: hatch; extra == 'dev'
|
|
36
37
|
Requires-Dist: mkdocs; extra == 'dev'
|
|
37
38
|
Requires-Dist: mkdocs-dracula-theme; extra == 'dev'
|
|
@@ -6,10 +6,11 @@ nav:
|
|
|
6
6
|
- 2. Defining Tables: 2_defining_tables.md
|
|
7
7
|
- 3. Building Queries: 3_building_queries.md
|
|
8
8
|
- 4. Relationships: 4_relationships.md
|
|
9
|
-
- 5. py4web: 5_py4web.md
|
|
9
|
+
- 5. py4web and web2py: 5_py4web.md
|
|
10
10
|
- 6. Migrations: 6_migrations.md
|
|
11
11
|
- 7. Configuration: 7_configuration.md
|
|
12
12
|
- 8. Mixins: 8_mixins.md
|
|
13
|
+
- 9. Function Memoization: 9_memoization.md
|
|
13
14
|
extra:
|
|
14
15
|
version:
|
|
15
16
|
default: stable
|
|
@@ -29,7 +29,7 @@ classifiers = [
|
|
|
29
29
|
dependencies = [
|
|
30
30
|
"pydal >= 20251012.3", # core
|
|
31
31
|
"dill < 1", # caching
|
|
32
|
-
"configuraptor >=
|
|
32
|
+
"configuraptor >= 2.0.1, < 3", # config
|
|
33
33
|
"Configurable-JSON < 2", # json dumping
|
|
34
34
|
"python-slugify < 9",
|
|
35
35
|
"legacy-cgi; python_version >= '3.13'",
|
|
@@ -63,6 +63,7 @@ all = [
|
|
|
63
63
|
dev = [
|
|
64
64
|
# build:
|
|
65
65
|
"hatch",
|
|
66
|
+
"ewok",
|
|
66
67
|
# test:
|
|
67
68
|
"su6[all]>=1.9.0",
|
|
68
69
|
"python-semantic-release < 8",
|
|
@@ -577,30 +577,15 @@ def memoize(
|
|
|
577
577
|
return cached, "cached"
|
|
578
578
|
# Cache miss - compute result
|
|
579
579
|
|
|
580
|
-
def track_execute(
|
|
580
|
+
def track_execute(_qb: "QueryBuilder[t.Any]", raw: Rows):
|
|
581
581
|
# find dependant table+id combinations, includes relationships:
|
|
582
582
|
deps.update(_determine_dependencies_auto(raw))
|
|
583
583
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
{
|
|
590
|
-
# original table
|
|
591
|
-
str(qb.model)
|
|
592
|
-
}
|
|
593
|
-
| {
|
|
594
|
-
# other tables in select()
|
|
595
|
-
_get_table_name(arg)
|
|
596
|
-
for arg in qb.select_args
|
|
597
|
-
}
|
|
598
|
-
| {
|
|
599
|
-
# other tables in relationships
|
|
600
|
-
str(r.table)
|
|
601
|
-
for r in qb.relationships.values()
|
|
602
|
-
}
|
|
603
|
-
)
|
|
584
|
+
related_tables = {
|
|
585
|
+
# skip qb.select, just look at the final fields in the raw data:
|
|
586
|
+
_get_table_name(field)
|
|
587
|
+
for field in raw.fields
|
|
588
|
+
}
|
|
604
589
|
|
|
605
590
|
# mark dependency for every relevant table in this query without id:
|
|
606
591
|
deps.update({(table, 0) for table in related_tables})
|
|
@@ -3,10 +3,8 @@ TypeDAL can be configured by a combination of pyproject.toml (static), env (dyna
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
-
import re
|
|
7
6
|
import typing as t
|
|
8
7
|
import warnings
|
|
9
|
-
from collections import defaultdict
|
|
10
8
|
from pathlib import Path
|
|
11
9
|
|
|
12
10
|
import tomli
|
|
@@ -16,6 +14,8 @@ from dotenv import dotenv_values, find_dotenv
|
|
|
16
14
|
|
|
17
15
|
from .types import AnyDict
|
|
18
16
|
|
|
17
|
+
from configuraptor.helpers import expand_env_vars_into_toml_values
|
|
18
|
+
|
|
19
19
|
if t.TYPE_CHECKING:
|
|
20
20
|
from edwh_migrate import Config as MigrateConfig
|
|
21
21
|
from pydal2sql.typer_support import Config as P2SConfig
|
|
@@ -244,88 +244,6 @@ def transform(data: AnyDict, prop: str) -> bool:
|
|
|
244
244
|
return False
|
|
245
245
|
|
|
246
246
|
|
|
247
|
-
def expand_posix_vars(posix_expr: str, context: dict[str, str]) -> str:
|
|
248
|
-
"""
|
|
249
|
-
Replace case-insensitive POSIX and Docker Compose-like environment variables in a string with their values.
|
|
250
|
-
|
|
251
|
-
Args:
|
|
252
|
-
posix_expr (str): The input string containing case-insensitive POSIX or Docker Compose-like variables.
|
|
253
|
-
context (dict): A dictionary containing variable names and their respective values.
|
|
254
|
-
|
|
255
|
-
Returns:
|
|
256
|
-
str: The string with replaced variable values.
|
|
257
|
-
|
|
258
|
-
See Also:
|
|
259
|
-
https://stackoverflow.com/questions/386934/how-to-evaluate-environment-variables-into-a-string-in-python
|
|
260
|
-
and ChatGPT
|
|
261
|
-
"""
|
|
262
|
-
env = defaultdict(lambda: "")
|
|
263
|
-
for key, value in context.items():
|
|
264
|
-
env[key.lower()] = value
|
|
265
|
-
|
|
266
|
-
# Regular expression to match "${VAR:default}" pattern
|
|
267
|
-
pattern = r"\$\{([^}]+)\}"
|
|
268
|
-
|
|
269
|
-
def replace_var(match: re.Match[t.Any]) -> str:
|
|
270
|
-
var_with_default = match.group(1)
|
|
271
|
-
var_name, default_value = var_with_default.split(":") if ":" in var_with_default else (var_with_default, "")
|
|
272
|
-
return env.get(var_name.lower(), default_value)
|
|
273
|
-
|
|
274
|
-
return re.sub(pattern, replace_var, posix_expr)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def expand_env_vars_into_toml_values(toml: AnyDict, env: AnyDict) -> None:
|
|
278
|
-
"""
|
|
279
|
-
Recursively expands POSIX/Docker Compose-like environment variables in a TOML dictionary.
|
|
280
|
-
|
|
281
|
-
This function traverses a TOML dictionary and expands POSIX/Docker Compose-like
|
|
282
|
-
environment variables (${VAR:default}) using values provided in the 'env' dictionary.
|
|
283
|
-
It performs in-place modification of the 'toml' dictionary.
|
|
284
|
-
|
|
285
|
-
Args:
|
|
286
|
-
toml (dict): A TOML dictionary with string values possibly containing environment variables.
|
|
287
|
-
env (dict): A dictionary containing environment variable names and their respective values.
|
|
288
|
-
|
|
289
|
-
Returns:
|
|
290
|
-
None: The function modifies the 'toml' dictionary in place.
|
|
291
|
-
|
|
292
|
-
Notes:
|
|
293
|
-
The function recursively traverses the 'toml' dictionary. If a value is a string or a list of strings,
|
|
294
|
-
it attempts to substitute any environment variables found within those strings using the 'env' dictionary.
|
|
295
|
-
|
|
296
|
-
Example:
|
|
297
|
-
toml_data = {
|
|
298
|
-
'key1': 'This has ${ENV_VAR:default}',
|
|
299
|
-
'key2': ['String with ${ANOTHER_VAR}', 'Another ${YET_ANOTHER_VAR}']
|
|
300
|
-
}
|
|
301
|
-
environment = {
|
|
302
|
-
'ENV_VAR': 'replaced_value',
|
|
303
|
-
'ANOTHER_VAR': 'value_1',
|
|
304
|
-
'YET_ANOTHER_VAR': 'value_2'
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
expand_env_vars_into_toml_values(toml_data, environment)
|
|
308
|
-
# 'toml_data' will be modified in place:
|
|
309
|
-
# {
|
|
310
|
-
# 'key1': 'This has replaced_value',
|
|
311
|
-
# 'key2': ['String with value_1', 'Another value_2']
|
|
312
|
-
# }
|
|
313
|
-
"""
|
|
314
|
-
if not toml or not env:
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
for key, var in toml.items():
|
|
318
|
-
if isinstance(var, dict):
|
|
319
|
-
expand_env_vars_into_toml_values(var, env)
|
|
320
|
-
elif isinstance(var, list):
|
|
321
|
-
toml[key] = [expand_posix_vars(_, env) for _ in var if isinstance(_, str)]
|
|
322
|
-
elif isinstance(var, str):
|
|
323
|
-
toml[key] = expand_posix_vars(var, env)
|
|
324
|
-
else:
|
|
325
|
-
# nothing to substitute
|
|
326
|
-
continue
|
|
327
|
-
|
|
328
|
-
|
|
329
247
|
def load_config(
|
|
330
248
|
connection_name: t.Optional[str] = None,
|
|
331
249
|
_use_pyproject: bool | str | None = True,
|
typedal-4.4.3/tasks.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Task automation using ewok (invoke-compatible)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from ewok import Context, task
|
|
7
|
+
|
|
8
|
+
# Compiled regex pattern for replacing the nav section in mkdocs.yml
|
|
9
|
+
NAV_SECTION_PATTERN = re.compile(r"nav:.*?(?=\n[a-z_]+:|$)", re.DOTALL)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extract_title(md_file: Path) -> str:
|
|
13
|
+
"""Extract the title from a markdown file's first heading."""
|
|
14
|
+
first_line = md_file.read_text(encoding="utf-8").split("\n", 1)[0].strip()
|
|
15
|
+
# Remove the leading # and any extra whitespace
|
|
16
|
+
return first_line.lstrip("#").strip()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_nav_entries() -> list[str]:
|
|
20
|
+
"""Generate nav entries from numbered markdown files in docs/."""
|
|
21
|
+
docs_dir = Path(__file__).parent / "docs"
|
|
22
|
+
|
|
23
|
+
# Find all numbered chapter files (supports 1-N digits)
|
|
24
|
+
chapters = [f for f in docs_dir.glob("*_*.md") if f.stem.split("_", 1)[0].isdigit()]
|
|
25
|
+
|
|
26
|
+
return [
|
|
27
|
+
f" - {extract_title(chapter)}: {chapter.name}"
|
|
28
|
+
for chapter in sorted(
|
|
29
|
+
chapters,
|
|
30
|
+
key=lambda f: int(f.stem.split("_", 1)[0]),
|
|
31
|
+
)
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@task
|
|
36
|
+
def update_docs_nav(ctx: Context) -> None:
|
|
37
|
+
"""Update mkdocs.yml nav section from actual markdown files to prevent sync issues."""
|
|
38
|
+
mkdocs_file = Path(__file__).parent / "mkdocs.yml"
|
|
39
|
+
|
|
40
|
+
content = mkdocs_file.read_text(encoding="utf-8")
|
|
41
|
+
|
|
42
|
+
# Generate new nav entries
|
|
43
|
+
nav_entries = generate_nav_entries()
|
|
44
|
+
new_nav_section = "nav:\n" + "\n".join(nav_entries)
|
|
45
|
+
|
|
46
|
+
# Replace the nav section
|
|
47
|
+
updated_content = NAV_SECTION_PATTERN.sub(new_nav_section, content)
|
|
48
|
+
|
|
49
|
+
mkdocs_file.write_text(updated_content, encoding="utf-8")
|
|
50
|
+
|
|
51
|
+
print(f"✓ Updated mkdocs.yml with {len(nav_entries)} chapters")
|
|
52
|
+
print("\nGenerated nav:")
|
|
53
|
+
for entry in nav_entries:
|
|
54
|
+
print(entry)
|
|
@@ -789,7 +789,7 @@ def test_memoize_with_empty_table():
|
|
|
789
789
|
|
|
790
790
|
# Memoize a function with empty table
|
|
791
791
|
def get_all_users() -> list[str]:
|
|
792
|
-
users = User.join().select(
|
|
792
|
+
users = User.join().select("user.name").execute() # also tests execute instead of collect
|
|
793
793
|
return [user[User.name] for user in users]
|
|
794
794
|
|
|
795
795
|
# First call with empty table
|
|
Binary file
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|