pyIntradel 0.0.4__tar.gz → 0.0.5__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.
- pyintradel-0.0.5/.github/check_version.sh +13 -0
- pyintradel-0.0.5/.github/workflows/build.yml +50 -0
- pyintradel-0.0.5/.github/workflows/publish-pypi.yml +37 -0
- pyintradel-0.0.5/.gitignore +131 -0
- {pyintradel-0.0.4 → pyintradel-0.0.5}/PKG-INFO +16 -21
- {pyintradel-0.0.4 → pyintradel-0.0.5}/README.md +1 -1
- {pyintradel-0.0.4 → pyintradel-0.0.5}/pyintradel/api/__init__.py +2 -5
- {pyintradel-0.0.4 → pyintradel-0.0.5}/pyintradel/api/parser.py +28 -28
- pyintradel-0.0.5/pyintradel/main.py +24 -0
- pyintradel-0.0.5/pyproject.toml +74 -0
- pyintradel-0.0.5/tests/__init__.py +5 -0
- pyintradel-0.0.5/tests/api_test.py +13 -0
- pyintradel-0.0.5/tests/mock.py +1216 -0
- pyintradel-0.0.4/pyIntradel.egg-info/PKG-INFO +0 -130
- pyintradel-0.0.4/pyIntradel.egg-info/SOURCES.txt +0 -15
- pyintradel-0.0.4/pyIntradel.egg-info/dependency_links.txt +0 -1
- pyintradel-0.0.4/pyIntradel.egg-info/not-zip-safe +0 -1
- pyintradel-0.0.4/pyIntradel.egg-info/requires.txt +0 -2
- pyintradel-0.0.4/pyIntradel.egg-info/top_level.txt +0 -1
- pyintradel-0.0.4/pyintradel/main.py +0 -25
- pyintradel-0.0.4/setup.cfg +0 -7
- pyintradel-0.0.4/setup.py +0 -34
- {pyintradel-0.0.4 → pyintradel-0.0.5}/LICENSE +0 -0
- {pyintradel-0.0.4 → pyintradel-0.0.5}/pyintradel/__init__.py +0 -0
- {pyintradel-0.0.4 → pyintradel-0.0.5}/pyintradel/api/towns.py +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
4
|
+
result=$(curl -s https://pypi.org/pypi/pyIntradel/json | jq -r '.releases | keys[]' | grep -w "$version")
|
|
5
|
+
|
|
6
|
+
if [ -z "$result" ]
|
|
7
|
+
then
|
|
8
|
+
echo Version "$version" not found in pypi, all good
|
|
9
|
+
exit 0
|
|
10
|
+
else
|
|
11
|
+
echo Version "$version" already exist in pypi
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
|
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
|
3
|
+
|
|
4
|
+
name: Build
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ['3.12', '3.13']
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
pip install -e .[dev]
|
|
31
|
+
|
|
32
|
+
- name: Lint with ruff
|
|
33
|
+
run: ruff check pyintradel tests
|
|
34
|
+
if: ${{ always() }}
|
|
35
|
+
|
|
36
|
+
- name: Check formatting with ruff
|
|
37
|
+
run: ruff format --check pyintradel tests
|
|
38
|
+
if: ${{ always() }}
|
|
39
|
+
|
|
40
|
+
- name: Type check with mypy
|
|
41
|
+
run: mypy pyintradel tests
|
|
42
|
+
if: ${{ always() }}
|
|
43
|
+
|
|
44
|
+
- name: Run tests
|
|
45
|
+
run: pytest
|
|
46
|
+
if: ${{ always() }}
|
|
47
|
+
|
|
48
|
+
- name: Check new pypi version is correct
|
|
49
|
+
run: (chmod +x .github/check_version.sh && sh .github/check_version.sh)
|
|
50
|
+
if: ${{ always() }}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Publish to pypi
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
environment: pypi
|
|
20
|
+
permissions:
|
|
21
|
+
# Required for PyPI trusted publishing (OIDC). No token/secret needed.
|
|
22
|
+
id-token: write
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.x'
|
|
30
|
+
- name: Install dependencies
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install build
|
|
34
|
+
- name: Build a binary wheel and a source tarball
|
|
35
|
+
run: python -m build --sdist --wheel --outdir dist/
|
|
36
|
+
- name: Publish package
|
|
37
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
|
|
131
|
+
.idea/
|
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyIntradel
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Python interface for Intradel
|
|
5
|
-
|
|
6
|
-
Author: Thomas Germain
|
|
7
|
-
Author-email: 12560542+thomasgermain@users.noreply.github.com
|
|
5
|
+
Project-URL: Homepage, https://github.com/thomasgermain/pyintradel
|
|
6
|
+
Author-email: Thomas Germain <12560542+thomasgermain@users.noreply.github.com>
|
|
8
7
|
License: MIT
|
|
9
|
-
|
|
8
|
+
License-File: LICENSE
|
|
10
9
|
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
14
|
Classifier: Topic :: Home Automation
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: aiohttp<4.0,>=3.14
|
|
17
|
+
Requires-Dist: beautifulsoup4<5.0,>=4.15
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: build>=1.5; extra == 'dev'
|
|
20
|
+
Requires-Dist: mypy>=2.1; extra == 'dev'
|
|
21
|
+
Requires-Dist: pytest>=9.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
15
23
|
Description-Content-Type: text/markdown
|
|
16
|
-
License-File: LICENSE
|
|
17
|
-
Requires-Dist: aiohttp<4.0.0,>=3.8.0
|
|
18
|
-
Requires-Dist: beautifulsoup4<5.0.0,>=4.11.0
|
|
19
|
-
Dynamic: author
|
|
20
|
-
Dynamic: author-email
|
|
21
|
-
Dynamic: classifier
|
|
22
|
-
Dynamic: description
|
|
23
|
-
Dynamic: description-content-type
|
|
24
|
-
Dynamic: home-page
|
|
25
|
-
Dynamic: license
|
|
26
|
-
Dynamic: license-file
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: summary
|
|
29
24
|
|
|
30
25
|
# pyIntradel
|
|
31
26
|
|
|
@@ -123,7 +118,7 @@ async with aiohttp.ClientSession() as sess:
|
|
|
123
118
|
|
|
124
119
|
### Command line
|
|
125
120
|
```bash
|
|
126
|
-
python3 main
|
|
121
|
+
python3 -m pyintradel.main user passw town
|
|
127
122
|
```
|
|
128
123
|
|
|
129
124
|
---
|
|
@@ -5,10 +5,7 @@ from typing import Any
|
|
|
5
5
|
|
|
6
6
|
import aiohttp
|
|
7
7
|
|
|
8
|
-
from . import parser
|
|
9
|
-
from . import towns
|
|
10
|
-
|
|
11
|
-
logging.basicConfig(level=logging.INFO, format="%(asctime)s:%(levelname)s:%(name)s: %(message)s")
|
|
8
|
+
from . import parser, towns
|
|
12
9
|
|
|
13
10
|
_LOGGER = logging.getLogger(__name__)
|
|
14
11
|
_URL = "https://www.intradel.be/particulier/"
|
|
@@ -22,7 +19,7 @@ async def get_data(
|
|
|
22
19
|
|
|
23
20
|
town_id = towns.TOWNS_MAP.get(town.upper())
|
|
24
21
|
if not town_id:
|
|
25
|
-
ValueError("Town not found", town)
|
|
22
|
+
raise ValueError("Town not found", town)
|
|
26
23
|
|
|
27
24
|
data = {"llogin": "YES", "login": login, "pass": password, "commune": town_id}
|
|
28
25
|
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from bs4 import BeautifulSoup
|
|
3
|
+
from bs4 import BeautifulSoup, Tag
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
def parse(response: str) -> list[Any]:
|
|
9
7
|
"""Parse response from intradel"""
|
|
10
|
-
results = []
|
|
8
|
+
results: list[Any] = []
|
|
11
9
|
soup = BeautifulSoup(response, features="html.parser")
|
|
12
10
|
|
|
13
|
-
if soup.
|
|
11
|
+
if soup.select_one('[name="pLogin"]') is not None:
|
|
14
12
|
raise ValueError("Wrong response received, login/password seems incorrect", response)
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.find(
|
|
19
|
-
.findChildren(attrs={"class": "post__content"})
|
|
20
|
-
)
|
|
21
|
-
for data in content:
|
|
22
|
-
result: Dict[str, Any] = {}
|
|
23
|
-
if data.find(name="h3") is not None:
|
|
14
|
+
for data in soup.select(".grid .row .post__content"):
|
|
15
|
+
result: dict[str, Any] = {}
|
|
16
|
+
if data.find("h3") is not None:
|
|
24
17
|
name = _name(data)
|
|
25
18
|
start_date = _start_date(data)
|
|
26
19
|
chip_id = _chip_id(data) or name
|
|
@@ -37,31 +30,38 @@ def parse(response: str) -> list[Any]:
|
|
|
37
30
|
return results
|
|
38
31
|
|
|
39
32
|
|
|
40
|
-
def
|
|
41
|
-
|
|
33
|
+
def _require_tag(node: object) -> Tag:
|
|
34
|
+
"""Narrow a bs4 lookup result to a Tag, failing loudly on unexpected markup."""
|
|
35
|
+
if not isinstance(node, Tag):
|
|
36
|
+
raise ValueError("Unexpected response structure from intradel")
|
|
37
|
+
return node
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _name(data: Tag) -> str:
|
|
41
|
+
return _require_tag(data.find("h3")).text.strip()
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
def _start_date(data:
|
|
45
|
-
info = data.
|
|
44
|
+
def _start_date(data: Tag) -> str:
|
|
45
|
+
info = data.find_all("p")
|
|
46
46
|
start_date = info[0]
|
|
47
47
|
if len(info) > 1:
|
|
48
48
|
start_date = info[3]
|
|
49
49
|
|
|
50
|
-
return
|
|
50
|
+
return start_date.text.split(":")[1].strip()
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def _chip_id(data:
|
|
53
|
+
def _chip_id(data: Tag) -> str | None:
|
|
54
54
|
chip_id = None
|
|
55
|
-
possible_chip_id = data.
|
|
55
|
+
possible_chip_id = data.find_all("p")
|
|
56
56
|
if len(possible_chip_id) > 1:
|
|
57
|
-
chip_id =
|
|
57
|
+
chip_id = possible_chip_id[1].text.split(":")[1].strip()
|
|
58
58
|
return chip_id
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def _details(data:
|
|
61
|
+
def _details(data: Tag) -> list[Any]:
|
|
62
62
|
attrs = []
|
|
63
|
-
for row in data.find(
|
|
64
|
-
tds = row.
|
|
63
|
+
for row in _require_tag(data.find("tbody")).find_all("tr"):
|
|
64
|
+
tds = row.find_all("td")
|
|
65
65
|
# When the list is empty, the website still includes an empty row.
|
|
66
66
|
# Let's just skip empty rows, as a valid row needs a date anyway.
|
|
67
67
|
if tds[0].text:
|
|
@@ -69,9 +69,9 @@ def _details(data: BeautifulSoup) -> list[Any]:
|
|
|
69
69
|
return attrs
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def _total(data:
|
|
72
|
+
def _total(data: Tag) -> str | None:
|
|
73
73
|
total = None
|
|
74
|
-
possible_total = data.find(
|
|
74
|
+
possible_total = _require_tag(data.find("tfoot")).find_all("td")
|
|
75
75
|
if possible_total:
|
|
76
|
-
total =
|
|
76
|
+
total = possible_total[2].text.split(" ")[0].strip()
|
|
77
77
|
return total
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from pyintradel import api
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def _run(login: str, password: str, town: str) -> None:
|
|
11
|
+
async with aiohttp.ClientSession() as sess:
|
|
12
|
+
json.dump(await api.get_data(sess, login, password, town), sys.stdout, indent=2)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> None:
|
|
16
|
+
if len(sys.argv) != 4:
|
|
17
|
+
print("Usage: pyintradel user pass town")
|
|
18
|
+
sys.exit(0)
|
|
19
|
+
|
|
20
|
+
asyncio.run(_run(sys.argv[1], sys.argv[2], sys.argv[3]))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
main()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyIntradel"
|
|
7
|
+
version = "0.0.5"
|
|
8
|
+
description = "Python interface for Intradel"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Thomas Germain", email = "12560542+thomasgermain@users.noreply.github.com" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"aiohttp>=3.14,<4.0",
|
|
17
|
+
"beautifulsoup4>=4.15,<5.0",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Development Status :: 4 - Beta",
|
|
22
|
+
"Programming Language :: Python",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Home Automation",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/thomasgermain/pyintradel"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = [
|
|
33
|
+
"ruff>=0.15",
|
|
34
|
+
"mypy>=2.1",
|
|
35
|
+
"pytest>=9.0",
|
|
36
|
+
"build>=1.5",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
pyintradel = "pyintradel.main:main"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["pyintradel"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff]
|
|
46
|
+
line-length = 100
|
|
47
|
+
target-version = "py312"
|
|
48
|
+
# Large scraped HTML fixture, kept verbatim for parser tests.
|
|
49
|
+
extend-exclude = ["tests/mock.py"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = [
|
|
53
|
+
"E", # pycodestyle errors
|
|
54
|
+
"W", # pycodestyle warnings
|
|
55
|
+
"F", # pyflakes
|
|
56
|
+
"I", # isort
|
|
57
|
+
"B", # flake8-bugbear
|
|
58
|
+
"C4", # flake8-comprehensions
|
|
59
|
+
"ERA", # eradicate (commented-out code)
|
|
60
|
+
"UP", # pyupgrade
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
strict = true
|
|
65
|
+
ignore_missing_imports = true
|
|
66
|
+
show_error_context = true
|
|
67
|
+
show_column_numbers = true
|
|
68
|
+
|
|
69
|
+
[[tool.mypy.overrides]]
|
|
70
|
+
module = "pyintradel.*"
|
|
71
|
+
implicit_reexport = true
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from pyintradel.api.parser import parse
|
|
4
|
+
from tests.mock import CORRECT_RESPONSE, INCORRECT_LOGIN
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ParsingTest(unittest.TestCase):
|
|
8
|
+
def test_parsing_correct(self) -> None:
|
|
9
|
+
result = parse(CORRECT_RESPONSE)
|
|
10
|
+
self.assertEqual(len(result), 3)
|
|
11
|
+
|
|
12
|
+
def test_parsing_login_error(self) -> None:
|
|
13
|
+
self.assertRaises(ValueError, parse, INCORRECT_LOGIN)
|