msgspec-extras 0.1.0__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.
@@ -0,0 +1,116 @@
1
+ # Common settings that generally should always be used with your language specific settings
2
+
3
+ # Auto detect text files and perform LF normalization
4
+ * text=auto
5
+
6
+ #
7
+ # The above will handle all files NOT found below
8
+ #
9
+
10
+ # Documents
11
+ *.bibtex text diff=bibtex
12
+ *.doc diff=astextplain
13
+ *.DOC diff=astextplain
14
+ *.docx diff=astextplain
15
+ *.DOCX diff=astextplain
16
+ *.dot diff=astextplain
17
+ *.DOT diff=astextplain
18
+ *.pdf diff=astextplain
19
+ *.PDF diff=astextplain
20
+ *.rtf diff=astextplain
21
+ *.RTF diff=astextplain
22
+ *.md text diff=markdown
23
+ *.mdx text diff=markdown
24
+ *.tex text diff=tex
25
+ *.adoc text
26
+ *.textile text
27
+ *.mustache text
28
+ *.csv text eol=crlf
29
+ *.tab text
30
+ *.tsv text
31
+ *.txt text
32
+ *.sql text
33
+ *.epub diff=astextplain
34
+
35
+ # Graphics
36
+ *.png binary
37
+ *.jpg binary
38
+ *.jpeg binary
39
+ *.gif binary
40
+ *.tif binary
41
+ *.tiff binary
42
+ *.ico binary
43
+ # SVG treated as text by default.
44
+ *.svg text
45
+ # If you want to treat it as binary,
46
+ # use the following line instead.
47
+ # *.svg binary
48
+ *.eps binary
49
+
50
+ # Scripts
51
+ *.bash text eol=lf
52
+ *.fish text eol=lf
53
+ *.sh text eol=lf
54
+ *.zsh text eol=lf
55
+ # These are explicitly windows files and should use crlf
56
+ *.bat text eol=crlf
57
+ *.cmd text eol=crlf
58
+ *.ps1 text eol=crlf
59
+
60
+ # Serialisation
61
+ *.json text
62
+ *.toml text
63
+ *.xml text
64
+ *.yaml text
65
+ *.yml text
66
+
67
+ # Archives
68
+ *.7z binary
69
+ *.gz binary
70
+ *.tar binary
71
+ *.tgz binary
72
+ *.zip binary
73
+
74
+ # Text files where line endings should be preserved
75
+ *.patch -text
76
+
77
+ #
78
+ # Exclude files from exporting
79
+ #
80
+
81
+ .gitattributes export-ignore
82
+ .gitignore export-ignore
83
+ .gitkeep export-ignore
84
+ # Apply override to all files in the directory
85
+ *.md linguist-detectable
86
+ # Basic .gitattributes for a python repo.
87
+
88
+ # Source files
89
+ # ============
90
+ *.pxd text diff=python
91
+ *.py text diff=python
92
+ *.py3 text diff=python
93
+ *.pyw text diff=python
94
+ *.pyx text diff=python
95
+ *.pyz text diff=python
96
+ *.pyi text diff=python
97
+
98
+ # Binary files
99
+ # ============
100
+ *.db binary
101
+ *.p binary
102
+ *.pkl binary
103
+ *.pickle binary
104
+ *.pyc binary export-ignore
105
+ *.pyo binary export-ignore
106
+ *.pyd binary
107
+
108
+ # Jupyter notebook
109
+ *.ipynb text eol=lf
110
+
111
+ # Note: .db, .p, and .pkl files are associated
112
+ # with the python modules ``pickle``, ``dbm.*``,
113
+ # ``shelve``, ``marshal``, ``anydbm``, & ``bsddb``
114
+ # (among others).
115
+ # Fix syntax highlighting on GitHub to allow comments
116
+ .vscode/*.json linguist-language=JSON-with-Comments
@@ -0,0 +1,21 @@
1
+ # =====Folders=====
2
+ __pycache__/
3
+ __pypackages__/
4
+ .idea/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .tox/
8
+ .venv/
9
+ build/
10
+ dist/
11
+ logs/
12
+ site/
13
+
14
+ # =====Files=====
15
+ *.iml
16
+ *.log
17
+ *.txt
18
+ .coverage
19
+ .envrc
20
+ .pdm-python
21
+ .python-version
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: msgspec-extras
3
+ Version: 0.1.0
4
+ Project-URL: Homepage, https://pypi.org/project/msgspec-extras
5
+ Project-URL: Issues, https://codefloe.com/buriedincode/msgspec-extras/issues
6
+ Project-URL: Source, https://codefloe.com/buriedincode/msgspec-extras
7
+ Author-email: BuriedInCode <buriedincode@duckpond.nz>
8
+ Maintainer-email: BuriedInCode <buriedincode@duckpond.nz>
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Natural Language :: English
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: msgspec>=0.21.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # msgspec-extras
26
+
27
+ [![PyPI - Python](https://img.shields.io/pypi/pyversions/msgspec-extras.svg?logo=Python&label=Python&style=flat-square)](https://pypi.org/p/msgspec-extras/)
28
+ [![PyPI - Status](https://img.shields.io/pypi/status/msgspec-extras.svg?logo=Python&label=Status&style=flat-square)](https://pypi.org/p/msgspec-extras/)
29
+ [![PyPI - Version](https://img.shields.io/pypi/v/msgspec-extras.svg?logo=Python&label=Version&style=flat-square)](https://pypi.org/p/msgspec-extras/)
30
+ [![PyPI - License](https://img.shields.io/pypi/l/msgspec-extras.svg?logo=Python&label=License&style=flat-square)](https://opensource.org/licenses/MIT)
31
+
32
+ [![prek](https://img.shields.io/badge/prek-enabled-informational?logo=prek&style=flat-square)](https://github.com/j178/prek)
33
+ [![Ruff](https://img.shields.io/badge/Ruff-enabled-informational?logo=ruff&style=flat-square)](https://github.com/astral-sh/ruff)
34
+ [![ty](https://img.shields.io/badge/ty-enabled-informational?logo=ruff&style=flat-square)](https://github.com/astral-sh/ty)
35
+
36
+ [![status-badge](https://ci.codefloe.com/api/v1/badges/1187/status.svg)](https://ci.codefloe.com/repos/1187)
37
+
38
+ A small collection of types that extend [msgspec](https://msgspec.dev)'s built-in type support.
39
+
40
+ ## Installation
41
+
42
+ ```sh
43
+ pdm add msgspec-extras
44
+ ```
45
+
46
+ ## Type Support
47
+
48
+ `msgspec-extras` supports **all msgspec native types** - see the full list in the [msgspec documentation](https://msgspec.dev/supported-types).
49
+ It also adds the following custom types.
50
+
51
+ ### Datetime
52
+
53
+ - `PastDate` - must be earlier than today
54
+ - `PastDatetime` - must be earlier than now
55
+ - `FutureDate` - must be later than today
56
+ - `FutureDatetime` - must be later than now
57
+
58
+ These types require the `enc_hook`/`dec_hook` to be passed to msgspec's encode/decode calls.
59
+
60
+ ```python
61
+ from datetime import datetime, date
62
+
63
+ import msgspec
64
+ from msgspec_extras import PastDate, PastDatetime, FutureDate, FutureDatetime, enc_hook, dec_hook
65
+
66
+ past_date = msgspec.json.decode('"2020-03-21"', type=PastDate, dec_hook=dec_hook)
67
+ past_date_json = msgspec.json.encode(past_date, enc_hook=enc_hook)
68
+
69
+ msgspec.json.decode('"2020-03-21"', type=FutureDate, dec_hook=dec_hook)
70
+ # Raises `msgspec.ValidationError` as the date isn't in the future
71
+ ```
72
+
73
+ ### Numeric
74
+
75
+ | Type | Constraint |
76
+ | ------------------ | ---------- |
77
+ | `PositiveInt` | `> 0` |
78
+ | `PositiveFloat` | `> 0` |
79
+ | `NonNegativeInt` | `>= 0` |
80
+ | `NonNegativeFloat` | `>= 0` |
81
+ | `NegativeInt` | `< 0` |
82
+ | `NegativeFloat` | `< 0` |
83
+ | `NonPositiveInt` | `<= 0` |
84
+ | `NonPositiveFloat` | `<= 0` |
85
+
86
+ ### String
87
+
88
+ - `HttpUrl` - must be a valid HTTP or HTTPS URL
89
+
90
+ ## Usage
91
+
92
+ The numeric and string types are just `Annotated` aliases over msgspec's native types (e.g. `Annotated[int, msgspec.Meta(gt=0)]`), so they need no hooks and work as drop-in replacements anywhere you'd use `int`, `float`, or `str`:
93
+
94
+ ```python
95
+ import msgspec
96
+ from msgspec_extras import PositiveFloat
97
+
98
+
99
+ class Product(msgspec.Struct):
100
+ name: str
101
+ price: PositiveFloat
102
+
103
+
104
+ product = msgspec.json.decode(b'{"name": "Widget", "price": 5.1}', type=Product)
105
+ msgspec.json.encode(product)
106
+ ```
107
+
108
+ ## Socials
109
+
110
+ [![Social - Matrix](https://img.shields.io/matrix/The-Dev-Environment:matrix.org?label=The-Dev-Environment&logo=matrix&style=for-the-badge)](https://matrix.to/#/#The-Dev-Environment:matrix.org)
@@ -0,0 +1,86 @@
1
+ # msgspec-extras
2
+
3
+ [![PyPI - Python](https://img.shields.io/pypi/pyversions/msgspec-extras.svg?logo=Python&label=Python&style=flat-square)](https://pypi.org/p/msgspec-extras/)
4
+ [![PyPI - Status](https://img.shields.io/pypi/status/msgspec-extras.svg?logo=Python&label=Status&style=flat-square)](https://pypi.org/p/msgspec-extras/)
5
+ [![PyPI - Version](https://img.shields.io/pypi/v/msgspec-extras.svg?logo=Python&label=Version&style=flat-square)](https://pypi.org/p/msgspec-extras/)
6
+ [![PyPI - License](https://img.shields.io/pypi/l/msgspec-extras.svg?logo=Python&label=License&style=flat-square)](https://opensource.org/licenses/MIT)
7
+
8
+ [![prek](https://img.shields.io/badge/prek-enabled-informational?logo=prek&style=flat-square)](https://github.com/j178/prek)
9
+ [![Ruff](https://img.shields.io/badge/Ruff-enabled-informational?logo=ruff&style=flat-square)](https://github.com/astral-sh/ruff)
10
+ [![ty](https://img.shields.io/badge/ty-enabled-informational?logo=ruff&style=flat-square)](https://github.com/astral-sh/ty)
11
+
12
+ [![status-badge](https://ci.codefloe.com/api/v1/badges/1187/status.svg)](https://ci.codefloe.com/repos/1187)
13
+
14
+ A small collection of types that extend [msgspec](https://msgspec.dev)'s built-in type support.
15
+
16
+ ## Installation
17
+
18
+ ```sh
19
+ pdm add msgspec-extras
20
+ ```
21
+
22
+ ## Type Support
23
+
24
+ `msgspec-extras` supports **all msgspec native types** - see the full list in the [msgspec documentation](https://msgspec.dev/supported-types).
25
+ It also adds the following custom types.
26
+
27
+ ### Datetime
28
+
29
+ - `PastDate` - must be earlier than today
30
+ - `PastDatetime` - must be earlier than now
31
+ - `FutureDate` - must be later than today
32
+ - `FutureDatetime` - must be later than now
33
+
34
+ These types require the `enc_hook`/`dec_hook` to be passed to msgspec's encode/decode calls.
35
+
36
+ ```python
37
+ from datetime import datetime, date
38
+
39
+ import msgspec
40
+ from msgspec_extras import PastDate, PastDatetime, FutureDate, FutureDatetime, enc_hook, dec_hook
41
+
42
+ past_date = msgspec.json.decode('"2020-03-21"', type=PastDate, dec_hook=dec_hook)
43
+ past_date_json = msgspec.json.encode(past_date, enc_hook=enc_hook)
44
+
45
+ msgspec.json.decode('"2020-03-21"', type=FutureDate, dec_hook=dec_hook)
46
+ # Raises `msgspec.ValidationError` as the date isn't in the future
47
+ ```
48
+
49
+ ### Numeric
50
+
51
+ | Type | Constraint |
52
+ | ------------------ | ---------- |
53
+ | `PositiveInt` | `> 0` |
54
+ | `PositiveFloat` | `> 0` |
55
+ | `NonNegativeInt` | `>= 0` |
56
+ | `NonNegativeFloat` | `>= 0` |
57
+ | `NegativeInt` | `< 0` |
58
+ | `NegativeFloat` | `< 0` |
59
+ | `NonPositiveInt` | `<= 0` |
60
+ | `NonPositiveFloat` | `<= 0` |
61
+
62
+ ### String
63
+
64
+ - `HttpUrl` - must be a valid HTTP or HTTPS URL
65
+
66
+ ## Usage
67
+
68
+ The numeric and string types are just `Annotated` aliases over msgspec's native types (e.g. `Annotated[int, msgspec.Meta(gt=0)]`), so they need no hooks and work as drop-in replacements anywhere you'd use `int`, `float`, or `str`:
69
+
70
+ ```python
71
+ import msgspec
72
+ from msgspec_extras import PositiveFloat
73
+
74
+
75
+ class Product(msgspec.Struct):
76
+ name: str
77
+ price: PositiveFloat
78
+
79
+
80
+ product = msgspec.json.decode(b'{"name": "Widget", "price": 5.1}', type=Product)
81
+ msgspec.json.encode(product)
82
+ ```
83
+
84
+ ## Socials
85
+
86
+ [![Social - Matrix](https://img.shields.io/matrix/The-Dev-Environment:matrix.org?label=The-Dev-Environment&logo=matrix&style=for-the-badge)](https://matrix.to/#/#The-Dev-Environment:matrix.org)
@@ -0,0 +1,33 @@
1
+ __all__ = [
2
+ "FutureDate",
3
+ "FutureDatetime",
4
+ "HttpUrl",
5
+ "NegativeFloat",
6
+ "NegativeInt",
7
+ "NonNegativeFloat",
8
+ "NonNegativeInt",
9
+ "NonPositiveFloat",
10
+ "NonPositiveInt",
11
+ "PastDate",
12
+ "PastDatetime",
13
+ "PositiveFloat",
14
+ "PositiveInt",
15
+ "__version__",
16
+ "dec_hook",
17
+ "enc_hook",
18
+ ]
19
+ __version__ = "0.1.0"
20
+
21
+ from msgspec_extras._hooks import dec_hook, enc_hook
22
+ from msgspec_extras.datetime import FutureDate, FutureDatetime, PastDate, PastDatetime
23
+ from msgspec_extras.numeric import (
24
+ NegativeFloat,
25
+ NegativeInt,
26
+ NonNegativeFloat,
27
+ NonNegativeInt,
28
+ NonPositiveFloat,
29
+ NonPositiveInt,
30
+ PositiveFloat,
31
+ PositiveInt,
32
+ )
33
+ from msgspec_extras.string import HttpUrl
@@ -0,0 +1,17 @@
1
+ __all__ = ["dec_hook", "enc_hook"]
2
+
3
+ from typing import Any
4
+
5
+ from msgspec_extras.datetime import FutureDate, FutureDatetime, PastDate, PastDatetime
6
+
7
+
8
+ def dec_hook(typ: type, obj: Any) -> Any: # noqa: ANN401
9
+ if typ in (PastDate, FutureDate, PastDatetime, FutureDatetime):
10
+ return typ(obj)
11
+ raise NotImplementedError(f"Type {typ.__name__} unsupported in dec_hook")
12
+
13
+
14
+ def enc_hook(obj: Any) -> Any: # noqa: ANN401
15
+ if isinstance(obj, (PastDate, FutureDate, PastDatetime, FutureDatetime)):
16
+ return obj.isoformat()
17
+ raise NotImplementedError(f"Encoding objects of type {type(obj).__name__} is unsupported")
@@ -0,0 +1,106 @@
1
+ __all__ = ["FutureDate", "FutureDatetime", "PastDate", "PastDatetime"]
2
+
3
+ from datetime import date, datetime
4
+
5
+ from msgspec import ValidationError
6
+
7
+ try:
8
+ from typing import Self # ty:ignore[unresolved-import]
9
+ except ImportError:
10
+ from typing_extensions import Self
11
+
12
+
13
+ class PastDate(date):
14
+ __slots__ = ()
15
+
16
+ def __new__(cls, value: date | str) -> Self:
17
+ if isinstance(value, date):
18
+ tmp: date = super().__new__(cls, value.year, value.month, value.day)
19
+ elif isinstance(value, str):
20
+ try:
21
+ tmp: date = date.fromisoformat(value)
22
+ except ValueError as err:
23
+ raise ValueError(f"Invalid date format: {value}") from err
24
+ else:
25
+ raise TypeError(f"PastDate must be a date or string, got {type(value).__name__}")
26
+ if tmp < date.today():
27
+ return tmp
28
+ raise ValidationError(f"PastDate must be in the past, got {tmp}")
29
+
30
+
31
+ class FutureDate(date):
32
+ __slots__ = ()
33
+
34
+ def __new__(cls, value: date | str) -> Self:
35
+ if isinstance(value, date):
36
+ tmp: date = super().__new__(cls, value.year, value.month, value.day)
37
+ elif isinstance(value, str):
38
+ try:
39
+ tmp: date = date.fromisoformat(value)
40
+ except ValueError as err:
41
+ raise ValueError(f"Invalid date format: {value}") from err
42
+ else:
43
+ raise TypeError(f"FutureDate must be a date or string, got {type(value).__name__}")
44
+ if tmp > date.today():
45
+ return tmp
46
+ raise ValidationError(f"FutureDate must be in the future, got {tmp}")
47
+
48
+
49
+ class PastDatetime(datetime):
50
+ __slots__ = ()
51
+
52
+ def __new__(cls, value: datetime | str) -> Self:
53
+ if isinstance(value, datetime):
54
+ tmp: datetime = super().__new__(
55
+ cls,
56
+ value.year,
57
+ value.month,
58
+ value.day,
59
+ value.hour,
60
+ value.minute,
61
+ value.second,
62
+ value.microsecond,
63
+ value.tzinfo,
64
+ )
65
+ elif isinstance(value, str):
66
+ try:
67
+ tmp: datetime = datetime.fromisoformat(value)
68
+ except ValueError as err:
69
+ raise ValueError(f"Invalid datetime format: {value}") from err
70
+ else:
71
+ raise TypeError(
72
+ f"PastDatetime must be a datetime or string, got {type(value).__name__}"
73
+ )
74
+ if tmp < datetime.now():
75
+ return tmp
76
+ raise ValidationError(f"PastDatetime must be in the past, got {tmp}")
77
+
78
+
79
+ class FutureDatetime(datetime):
80
+ __slots__ = ()
81
+
82
+ def __new__(cls, value: datetime | str) -> Self:
83
+ if isinstance(value, datetime):
84
+ tmp: datetime = super().__new__(
85
+ cls,
86
+ value.year,
87
+ value.month,
88
+ value.day,
89
+ value.hour,
90
+ value.minute,
91
+ value.second,
92
+ value.microsecond,
93
+ value.tzinfo,
94
+ )
95
+ elif isinstance(value, str):
96
+ try:
97
+ tmp: datetime = datetime.fromisoformat(value)
98
+ except ValueError as err:
99
+ raise ValueError(f"Invalid datetime format: {value}") from err
100
+ else:
101
+ raise TypeError(
102
+ f"FutureDatetime must be a datetime or string, got {type(value).__name__}"
103
+ )
104
+ if tmp > datetime.now():
105
+ return tmp
106
+ raise ValidationError(f"FutureDatetime must be in the future, got {tmp}")
@@ -0,0 +1,24 @@
1
+ __all__ = [
2
+ "NegativeFloat",
3
+ "NegativeInt",
4
+ "NonNegativeFloat",
5
+ "NonNegativeInt",
6
+ "NonPositiveFloat",
7
+ "NonPositiveInt",
8
+ "PositiveFloat",
9
+ "PositiveInt",
10
+ ]
11
+
12
+ from typing import Annotated
13
+
14
+ from msgspec import Meta
15
+
16
+ PositiveInt = Annotated[int, Meta(gt=0, description="Integer greater than 0")]
17
+ NegativeInt = Annotated[int, Meta(lt=0, description="Integer less than 0")]
18
+ NonNegativeInt = Annotated[int, Meta(ge=0, description="Integer greater than or equal to 0")]
19
+ NonPositiveInt = Annotated[int, Meta(le=0, description="Integer less than or equal to 0")]
20
+
21
+ PositiveFloat = Annotated[float, Meta(gt=0.0, description="Float greater than 0.0")]
22
+ NegativeFloat = Annotated[float, Meta(lt=0.0, description="Float less than 0.0")]
23
+ NonNegativeFloat = Annotated[float, Meta(ge=0.0, description="Float greater than or equal to 0.0")]
24
+ NonPositiveFloat = Annotated[float, Meta(le=0.0, description="Float less than or equal to 0.0")]
@@ -0,0 +1,7 @@
1
+ __all__ = ["HttpUrl"]
2
+
3
+ from typing import Annotated
4
+
5
+ from msgspec import Meta
6
+
7
+ HttpUrl = Annotated[str, Meta(max_length=2083, pattern=r"^https?://[^\s/$.?#].[^\s]*$")]
@@ -0,0 +1,52 @@
1
+ [[repos]]
2
+ hooks = [
3
+ {args = ["--all", "--ignore-case", "--in-place", "--trailing-comma-inline-array"], entry = "toml-sort", exclude = ".*.lock|pylock.toml", id = "toml-sort", language = "system", name = "toml sort", types = ["toml"]},
4
+ {args = ["--number", "--wrap=keep"], entry = "mdformat", id = "mdformat", language = "system", name = "mdformat", types = ["markdown"]},
5
+ {entry = "ruff check .", id = "ruff-check", language = "system", name = "ruff check", pass_filenames = false, types = ["python"]},
6
+ {entry = "ruff format .", id = "ruff-format", language = "system", name = "ruff format", pass_filenames = false, types = ["python"]},
7
+ {entry = "ty check .", id = "ty", language = "system", name = "ty", pass_filenames = false, types = ["python"]},
8
+ ]
9
+ repo = "local"
10
+
11
+ [[repos]]
12
+ hooks = [
13
+ {id = "check-ast"},
14
+ {id = "check-builtin-literals"},
15
+ {id = "check-docstring-first"},
16
+ {id = "debug-statements"},
17
+ {id = "forbid-submodules"},
18
+ ]
19
+ repo = "https://github.com/pre-commit/pre-commit-hooks"
20
+ rev = "v6.0.0"
21
+
22
+ [[repos]]
23
+ hooks = [
24
+ {args = ["--allow-multiple-documents"], id = "check-yaml"},
25
+ {args = ["--assume-in-merge"], id = "check-merge-conflict"},
26
+ # {args = ["--autofix", "--indent=2", "--no-ensure-ascii"], id = "pretty-format-json"},
27
+ {args = ["--markdown-linebreak-ext=md"], id = "trailing-whitespace"},
28
+ {exclude_types = ["json", "svg", "xml"], id = "end-of-file-fixer"},
29
+ {id = "check-added-large-files"},
30
+ {id = "check-case-conflict"},
31
+ # {id = "check-executables-have-shebangs"},
32
+ # {id = "check-illegal-windows-names"},
33
+ # {id = "check-json"},
34
+ # {id = "check-json5"},
35
+ {id = "check-shebang-scripts-are-executable"},
36
+ # {id = "check-symlinks"},
37
+ {id = "check-toml"},
38
+ {id = "check-vcs-permalinks"},
39
+ # {id = "check-xml"},
40
+ {id = "destroyed-symlinks"},
41
+ {id = "detect-private-key"},
42
+ {id = "fix-byte-order-marker"},
43
+ {id = "mixed-line-ending"},
44
+ ]
45
+ repo = "builtin"
46
+
47
+ [[repos]]
48
+ hooks = [
49
+ {id = "check-hooks-apply"},
50
+ {id = "check-useless-excludes"},
51
+ ]
52
+ repo = "meta"