ccflow-email 0.2.0__tar.gz → 0.2.2__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.
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/.gitignore +31 -28
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/PKG-INFO +9 -8
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/ccflow_email/__init__.py +1 -1
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/ccflow_email/model.py +21 -40
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/ccflow_email/tests/test_model.py +26 -0
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/pyproject.toml +45 -26
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/LICENSE +0 -0
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/README.md +0 -0
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/ccflow_email/publisher.py +0 -0
- {ccflow_email-0.2.0 → ccflow_email-0.2.2}/ccflow_email/tests/conftest.py +0 -0
|
@@ -5,12 +5,22 @@ __pycache__/
|
|
|
5
5
|
|
|
6
6
|
# C extensions
|
|
7
7
|
*.a
|
|
8
|
+
*.o
|
|
9
|
+
*.dSYM
|
|
8
10
|
*.so
|
|
9
11
|
*.obj
|
|
10
12
|
*.dll
|
|
11
13
|
*.exp
|
|
12
14
|
*.lib
|
|
13
15
|
|
|
16
|
+
# vcpkg
|
|
17
|
+
vcpkg/
|
|
18
|
+
vcpkg_installed/
|
|
19
|
+
|
|
20
|
+
# Rust
|
|
21
|
+
target
|
|
22
|
+
target-capi
|
|
23
|
+
|
|
14
24
|
# Distribution / packaging
|
|
15
25
|
.Python
|
|
16
26
|
build/
|
|
@@ -55,26 +65,12 @@ junit.xml
|
|
|
55
65
|
.hypothesis/
|
|
56
66
|
.pytest_cache/
|
|
57
67
|
|
|
58
|
-
#
|
|
59
|
-
*.mo
|
|
60
|
-
*.pot
|
|
61
|
-
|
|
62
|
-
# Django stuff:
|
|
68
|
+
# Django
|
|
63
69
|
*.log
|
|
64
70
|
local_settings.py
|
|
65
71
|
db.sqlite3
|
|
66
72
|
db.sqlite3-journal
|
|
67
73
|
|
|
68
|
-
# Flask stuff:
|
|
69
|
-
instance/
|
|
70
|
-
.webassets-cache
|
|
71
|
-
|
|
72
|
-
# Scrapy stuff:
|
|
73
|
-
.scrapy
|
|
74
|
-
|
|
75
|
-
# PyBuilder
|
|
76
|
-
target/
|
|
77
|
-
|
|
78
74
|
# IPython
|
|
79
75
|
profile_default/
|
|
80
76
|
ipython_config.py
|
|
@@ -85,15 +81,12 @@ ipython_config.py
|
|
|
85
81
|
# pipenv
|
|
86
82
|
Pipfile.lock
|
|
87
83
|
|
|
88
|
-
#
|
|
89
|
-
__pypackages__/
|
|
90
|
-
|
|
91
|
-
# Celery stuff
|
|
84
|
+
# Celery
|
|
92
85
|
celerybeat-schedule
|
|
93
86
|
celerybeat.pid
|
|
94
87
|
|
|
95
|
-
#
|
|
96
|
-
|
|
88
|
+
# Airspeed Velocity
|
|
89
|
+
.asv
|
|
97
90
|
|
|
98
91
|
# Environments
|
|
99
92
|
.env
|
|
@@ -126,11 +119,12 @@ dmypy.json
|
|
|
126
119
|
/site
|
|
127
120
|
index.md
|
|
128
121
|
docs/_build/
|
|
129
|
-
docs/src/_build/
|
|
130
122
|
docs/api
|
|
131
|
-
docs/index.md
|
|
132
123
|
docs/html
|
|
124
|
+
docs/index.md
|
|
133
125
|
docs/jupyter_execute
|
|
126
|
+
docs/src/_build/
|
|
127
|
+
docs/superpowers
|
|
134
128
|
index.md
|
|
135
129
|
|
|
136
130
|
# JS
|
|
@@ -141,19 +135,28 @@ js/node_modules
|
|
|
141
135
|
js/test-results
|
|
142
136
|
js/playwright-report
|
|
143
137
|
js/*.tgz
|
|
144
|
-
ccflow_email/extension
|
|
145
138
|
|
|
146
139
|
# Jupyter
|
|
147
140
|
.ipynb_checkpoints
|
|
148
141
|
.autoversion
|
|
149
142
|
Untitled*.ipynb
|
|
150
|
-
|
|
151
|
-
!ccflow_email/extension/install.json
|
|
143
|
+
ccflow_email/extension
|
|
152
144
|
ccflow_email/nbextension
|
|
153
145
|
ccflow_email/labextension
|
|
154
146
|
|
|
147
|
+
# Emscripten SDK (locally installed)
|
|
148
|
+
emsdk
|
|
149
|
+
.pyodide_build/
|
|
150
|
+
|
|
155
151
|
# Mac
|
|
156
152
|
.DS_Store
|
|
157
153
|
|
|
158
|
-
#
|
|
159
|
-
|
|
154
|
+
# Hydra
|
|
155
|
+
/outputs/
|
|
156
|
+
/multirun/
|
|
157
|
+
|
|
158
|
+
# AI
|
|
159
|
+
ROADMAP.md
|
|
160
|
+
AGENTS.md
|
|
161
|
+
.github/hooks/sdlc.json
|
|
162
|
+
.superpowers
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ccflow-email
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: ccflow models for email
|
|
5
5
|
Project-URL: Repository, https://github.com/1kbgz/ccflow-email
|
|
6
6
|
Project-URL: Homepage, https://github.com/1kbgz/ccflow-email
|
|
@@ -10,28 +10,29 @@ License-File: LICENSE
|
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
17
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
18
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
20
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.11
|
|
21
20
|
Requires-Dist: ccflow
|
|
22
21
|
Requires-Dist: emails
|
|
23
22
|
Provides-Extra: develop
|
|
24
23
|
Requires-Dist: build; extra == 'develop'
|
|
25
24
|
Requires-Dist: bump-my-version; extra == 'develop'
|
|
26
|
-
Requires-Dist: check-
|
|
27
|
-
Requires-Dist: codespell
|
|
25
|
+
Requires-Dist: check-dist; extra == 'develop'
|
|
26
|
+
Requires-Dist: codespell; extra == 'develop'
|
|
28
27
|
Requires-Dist: hatchling; extra == 'develop'
|
|
28
|
+
Requires-Dist: mdformat; extra == 'develop'
|
|
29
|
+
Requires-Dist: mdformat-myst; extra == 'develop'
|
|
29
30
|
Requires-Dist: mdformat-tables>=1; extra == 'develop'
|
|
30
|
-
Requires-Dist: mdformat<1.1,>=0.7.22; extra == 'develop'
|
|
31
31
|
Requires-Dist: pytest; extra == 'develop'
|
|
32
32
|
Requires-Dist: pytest-cov; extra == 'develop'
|
|
33
|
-
Requires-Dist: ruff
|
|
33
|
+
Requires-Dist: ruff; extra == 'develop'
|
|
34
34
|
Requires-Dist: twine; extra == 'develop'
|
|
35
|
+
Requires-Dist: ty; extra == 'develop'
|
|
35
36
|
Requires-Dist: uv; extra == 'develop'
|
|
36
37
|
Requires-Dist: wheel; extra == 'develop'
|
|
37
38
|
Description-Content-Type: text/markdown
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
from typing import Union
|
|
2
|
-
|
|
3
1
|
from ccflow import BaseModel
|
|
4
2
|
from pydantic import Field, field_validator, model_validator
|
|
5
3
|
|
|
6
4
|
__all__ = (
|
|
7
|
-
"Message",
|
|
8
5
|
"SMTP",
|
|
9
6
|
"Attachment",
|
|
10
7
|
"Email",
|
|
8
|
+
"Message",
|
|
11
9
|
)
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class Message(BaseModel):
|
|
15
|
-
content: str = Field(default=None, description="HTML content of the email")
|
|
16
|
-
subject: str = Field(default=None, description="Subject of the email")
|
|
17
|
-
from_:
|
|
18
|
-
to_:
|
|
19
|
-
cc:
|
|
20
|
-
bcc:
|
|
13
|
+
content: str | None = Field(default=None, description="HTML content of the email")
|
|
14
|
+
subject: str | None = Field(default=None, description="Subject of the email")
|
|
15
|
+
from_: tuple[str, str] | str | None = Field(default=None, description="Sender email address")
|
|
16
|
+
to_: tuple[str, str] | str | None = Field(default=None, description="Recipient email address")
|
|
17
|
+
cc: tuple[str, str] | str | None = Field(default=None, description="CC email address")
|
|
18
|
+
bcc: tuple[str, str] | str | None = Field(default=None, description="BCC email address")
|
|
21
19
|
|
|
22
20
|
@field_validator("from_")
|
|
21
|
+
@classmethod
|
|
23
22
|
def _validate_from(cls, v):
|
|
24
23
|
# If tuple, must be (name, email)
|
|
25
24
|
if isinstance(v, tuple):
|
|
@@ -30,6 +29,7 @@ class Message(BaseModel):
|
|
|
30
29
|
return v
|
|
31
30
|
|
|
32
31
|
@field_validator("to_")
|
|
32
|
+
@classmethod
|
|
33
33
|
def _validate_to(cls, v):
|
|
34
34
|
# If tuple, must be (name, email)
|
|
35
35
|
if isinstance(v, tuple):
|
|
@@ -40,6 +40,7 @@ class Message(BaseModel):
|
|
|
40
40
|
return v
|
|
41
41
|
|
|
42
42
|
@field_validator("cc")
|
|
43
|
+
@classmethod
|
|
43
44
|
def _validate_cc(cls, v):
|
|
44
45
|
# If tuple, must be (name, email)
|
|
45
46
|
if isinstance(v, tuple):
|
|
@@ -50,6 +51,7 @@ class Message(BaseModel):
|
|
|
50
51
|
return v
|
|
51
52
|
|
|
52
53
|
@field_validator("bcc")
|
|
54
|
+
@classmethod
|
|
53
55
|
def _validate_bcc(cls, v):
|
|
54
56
|
# If tuple, must be (name, email)
|
|
55
57
|
if isinstance(v, tuple):
|
|
@@ -62,33 +64,12 @@ class Message(BaseModel):
|
|
|
62
64
|
|
|
63
65
|
class SMTP(BaseModel):
|
|
64
66
|
host: str = Field(..., description="SMTP server host")
|
|
65
|
-
port: int = Field(default=25, description="SMTP server port")
|
|
66
|
-
user: str = Field(default=None, description="SMTP server username")
|
|
67
|
-
password: str = Field(default=None, description="SMTP server password")
|
|
68
|
-
tls: bool = Field(default=False, description="Use TLS for SMTP connection")
|
|
69
|
-
ssl: bool = Field(default=False, description="Use SSL for SMTP connection")
|
|
70
|
-
timeout: int = Field(default=30, description="Timeout for SMTP connection in seconds")
|
|
71
|
-
|
|
72
|
-
@field_validator("port")
|
|
73
|
-
def validate_port(cls, v):
|
|
74
|
-
# Must be a normal smtp port
|
|
75
|
-
if v not in [25, 465, 587]:
|
|
76
|
-
raise ValueError("Invalid SMTP port")
|
|
77
|
-
return v
|
|
78
|
-
|
|
79
|
-
@field_validator("tls")
|
|
80
|
-
def validate_tls(cls, v, info):
|
|
81
|
-
# If port is 465, tls must be False
|
|
82
|
-
if info.data.get("port") == 465 and v:
|
|
83
|
-
raise ValueError("TLS cannot be True when port is 465")
|
|
84
|
-
return v
|
|
85
|
-
|
|
86
|
-
@field_validator("ssl")
|
|
87
|
-
def validate_ssl(cls, v, info):
|
|
88
|
-
# If port is 587 or 25, ssl must be False
|
|
89
|
-
if info.data.get("port") in [25, 587] and v:
|
|
90
|
-
raise ValueError("SSL cannot be True when port is 25 or 587")
|
|
91
|
-
return v
|
|
67
|
+
port: int | None = Field(default=25, description="SMTP server port")
|
|
68
|
+
user: str | None = Field(default=None, description="SMTP server username")
|
|
69
|
+
password: str | None = Field(default=None, repr=False, description="SMTP server password")
|
|
70
|
+
tls: bool | None = Field(default=False, description="Use TLS for SMTP connection")
|
|
71
|
+
ssl: bool | None = Field(default=False, description="Use SSL for SMTP connection")
|
|
72
|
+
timeout: int | None = Field(default=30, description="Timeout for SMTP connection in seconds")
|
|
92
73
|
|
|
93
74
|
|
|
94
75
|
class Attachment(BaseModel):
|
|
@@ -100,7 +81,7 @@ class Attachment(BaseModel):
|
|
|
100
81
|
class Email(BaseModel):
|
|
101
82
|
message: Message = Field(description="Email message details")
|
|
102
83
|
smtp: SMTP = Field(description="SMTP server configuration")
|
|
103
|
-
attachments: list[Attachment] = Field(default_factory=list, description="List of email attachments")
|
|
84
|
+
attachments: list[Attachment] | None = Field(default_factory=list, description="List of email attachments")
|
|
104
85
|
|
|
105
86
|
@model_validator(mode="after")
|
|
106
87
|
def _validate_from(self):
|
|
@@ -112,7 +93,7 @@ class Email(BaseModel):
|
|
|
112
93
|
self.smtp.user = self.message.from_[1] if isinstance(self.message.from_, tuple) else self.message.from_
|
|
113
94
|
return self
|
|
114
95
|
|
|
115
|
-
def send(self, to:
|
|
96
|
+
def send(self, to: str | tuple[str | None, str] | list[str | tuple[str | None, str]] | None = None, render: dict | None = None):
|
|
116
97
|
# NOTE: defer import
|
|
117
98
|
from emails import Message as EmailMessage
|
|
118
99
|
|
|
@@ -132,10 +113,10 @@ class Email(BaseModel):
|
|
|
132
113
|
bcc=self.message.bcc,
|
|
133
114
|
)
|
|
134
115
|
|
|
135
|
-
for attachment in self.attachments:
|
|
116
|
+
for attachment in self.attachments or ():
|
|
136
117
|
msg.attach(filename=attachment.filename, content_disposition=attachment.content_disposition, data=attachment.data)
|
|
137
118
|
|
|
138
|
-
smtp_config = self.smtp.model_dump(exclude_none=True, exclude=
|
|
119
|
+
smtp_config = self.smtp.model_dump(exclude_unset=True, exclude_none=True, exclude={"type_"})
|
|
139
120
|
smtp_config["fail_silently"] = False
|
|
140
121
|
response = msg.send(to=to, render=render or {}, smtp=smtp_config)
|
|
141
122
|
return response
|
|
@@ -15,6 +15,7 @@ class TestEmail:
|
|
|
15
15
|
)
|
|
16
16
|
assert email.message.subject == "Test Email"
|
|
17
17
|
assert email.smtp.host == "smtp.example.com"
|
|
18
|
+
assert email.attachments is not None
|
|
18
19
|
assert email.attachments[0].filename == "test.txt"
|
|
19
20
|
|
|
20
21
|
@pytest.mark.skipif(not environ.get("SMTP_HOST"), reason="SMTP server not configured")
|
|
@@ -73,3 +74,28 @@ class TestEmail:
|
|
|
73
74
|
|
|
74
75
|
assert response.success is True
|
|
75
76
|
assert response.status_text == b"Message received"
|
|
77
|
+
|
|
78
|
+
def test_send_without_attachments(self):
|
|
79
|
+
email = Email(
|
|
80
|
+
message=Message(content="Hello", subject="Test", from_="sender@example.com"),
|
|
81
|
+
smtp=SMTP(host="smtp.example.com"),
|
|
82
|
+
attachments=None,
|
|
83
|
+
)
|
|
84
|
+
recipient = ("Recipient", "recipient@example.com")
|
|
85
|
+
with patch("emails.Message") as message:
|
|
86
|
+
response = email.send(to=recipient)
|
|
87
|
+
message.assert_called_once_with(html="Hello", subject="Test", mail_from="sender@example.com", mail_to=recipient, cc=None, bcc=None)
|
|
88
|
+
message.return_value.attach.assert_not_called()
|
|
89
|
+
assert response is message.return_value.send.return_value
|
|
90
|
+
assert "type_" not in message.return_value.send.call_args.kwargs["smtp"]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TestSMTP:
|
|
94
|
+
def test_repr_omits_password(self):
|
|
95
|
+
smtp = SMTP(host="smtp.example.com", user="user", password="smtp-secret")
|
|
96
|
+
email = Email(message=Message(content="Hi", subject="Test", from_="user"), smtp=smtp)
|
|
97
|
+
|
|
98
|
+
for value in (smtp, email):
|
|
99
|
+
assert "smtp-secret" not in repr(value)
|
|
100
|
+
assert "smtp-secret" not in str(value)
|
|
101
|
+
assert "smtp.example.com" in repr(smtp)
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
[build-system]
|
|
2
|
-
requires = [
|
|
3
|
-
|
|
2
|
+
requires = [
|
|
3
|
+
"hatchling",
|
|
4
|
+
]
|
|
5
|
+
build-backend = "hatchling.build"
|
|
4
6
|
|
|
5
7
|
[project]
|
|
6
8
|
name = "ccflow-email"
|
|
7
|
-
authors = [
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "1kbgz", email = "dev@1kbgz.com"},
|
|
11
|
+
]
|
|
8
12
|
description = "ccflow models for email"
|
|
9
13
|
readme = "README.md"
|
|
10
14
|
license = { text = "Apache-2.0" }
|
|
11
|
-
version = "0.2.
|
|
12
|
-
requires-python = ">=3.
|
|
15
|
+
version = "0.2.2"
|
|
16
|
+
requires-python = ">=3.11"
|
|
13
17
|
keywords = []
|
|
14
18
|
|
|
15
19
|
classifiers = [
|
|
@@ -18,7 +22,6 @@ classifiers = [
|
|
|
18
22
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
19
23
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
20
24
|
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.10",
|
|
22
25
|
"Programming Language :: Python :: 3.11",
|
|
23
26
|
"Programming Language :: Python :: 3.12",
|
|
24
27
|
"Programming Language :: Python :: 3.13",
|
|
@@ -34,15 +37,17 @@ dependencies = [
|
|
|
34
37
|
develop = [
|
|
35
38
|
"build",
|
|
36
39
|
"bump-my-version",
|
|
37
|
-
"check-
|
|
38
|
-
"codespell
|
|
40
|
+
"check-dist",
|
|
41
|
+
"codespell",
|
|
39
42
|
"hatchling",
|
|
40
|
-
"mdformat
|
|
43
|
+
"mdformat",
|
|
44
|
+
"mdformat-myst",
|
|
41
45
|
"mdformat-tables>=1",
|
|
42
46
|
"pytest",
|
|
43
47
|
"pytest-cov",
|
|
44
|
-
"ruff
|
|
48
|
+
"ruff",
|
|
45
49
|
"twine",
|
|
50
|
+
"ty",
|
|
46
51
|
"uv",
|
|
47
52
|
"wheel",
|
|
48
53
|
]
|
|
@@ -54,7 +59,7 @@ Repository = "https://github.com/1kbgz/ccflow-email"
|
|
|
54
59
|
Homepage = "https://github.com/1kbgz/ccflow-email"
|
|
55
60
|
|
|
56
61
|
[tool.bumpversion]
|
|
57
|
-
current_version = "0.2.
|
|
62
|
+
current_version = "0.2.2"
|
|
58
63
|
commit = true
|
|
59
64
|
tag = true
|
|
60
65
|
commit_args = "-s"
|
|
@@ -69,18 +74,12 @@ filename = "pyproject.toml"
|
|
|
69
74
|
search = 'version = "{current_version}"'
|
|
70
75
|
replace = 'version = "{new_version}"'
|
|
71
76
|
|
|
72
|
-
[tool.check-manifest]
|
|
73
|
-
ignore = [
|
|
74
|
-
".copier-answers.yaml",
|
|
75
|
-
"Makefile",
|
|
76
|
-
"docs/**/*",
|
|
77
|
-
]
|
|
78
|
-
|
|
79
77
|
[tool.coverage.run]
|
|
80
78
|
branch = true
|
|
81
79
|
omit = [
|
|
82
|
-
"ccflow_email/tests/integration
|
|
80
|
+
"ccflow_email/tests/integration/*",
|
|
83
81
|
]
|
|
82
|
+
|
|
84
83
|
[tool.coverage.report]
|
|
85
84
|
exclude_also = [
|
|
86
85
|
"raise NotImplementedError",
|
|
@@ -97,28 +96,48 @@ artifacts = []
|
|
|
97
96
|
src = "/"
|
|
98
97
|
|
|
99
98
|
[tool.hatch.build.targets.sdist]
|
|
100
|
-
packages = [
|
|
99
|
+
packages = [
|
|
100
|
+
"ccflow_email",
|
|
101
|
+
]
|
|
101
102
|
|
|
102
103
|
[tool.hatch.build.targets.wheel]
|
|
103
|
-
packages = [
|
|
104
|
+
packages = [
|
|
105
|
+
"ccflow_email",
|
|
106
|
+
]
|
|
104
107
|
|
|
105
108
|
[tool.hatch.build.targets.wheel.shared-data]
|
|
106
109
|
|
|
107
110
|
[tool.pytest.ini_options]
|
|
108
|
-
addopts = [
|
|
111
|
+
addopts = [
|
|
112
|
+
"-vvv",
|
|
113
|
+
"--junitxml=junit.xml",
|
|
114
|
+
]
|
|
109
115
|
testpaths = "ccflow_email/tests"
|
|
110
116
|
|
|
111
117
|
[tool.ruff]
|
|
112
118
|
line-length = 150
|
|
113
119
|
|
|
114
120
|
[tool.ruff.lint]
|
|
115
|
-
extend-select = [
|
|
121
|
+
extend-select = [
|
|
122
|
+
"I",
|
|
123
|
+
]
|
|
116
124
|
|
|
117
125
|
[tool.ruff.lint.isort]
|
|
118
126
|
combine-as-imports = true
|
|
119
127
|
default-section = "third-party"
|
|
120
|
-
known-first-party = [
|
|
121
|
-
|
|
128
|
+
known-first-party = [
|
|
129
|
+
"ccflow_email",
|
|
130
|
+
]
|
|
131
|
+
section-order = [
|
|
132
|
+
"future",
|
|
133
|
+
"standard-library",
|
|
134
|
+
"third-party",
|
|
135
|
+
"first-party",
|
|
136
|
+
"local-folder",
|
|
137
|
+
]
|
|
122
138
|
|
|
123
139
|
[tool.ruff.lint.per-file-ignores]
|
|
124
|
-
"__init__.py" = [
|
|
140
|
+
"__init__.py" = [
|
|
141
|
+
"F401",
|
|
142
|
+
"F403",
|
|
143
|
+
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|