Skip to content

Commit 4dd0658

Browse files
Merge pull request #15 from HarlemSquirrel/add-types
Add types and drop support for Python < 3.10
2 parents 23f0fc5 + f85e412 commit 4dd0658

File tree

8 files changed

+78
-33
lines changed

8 files changed

+78
-33
lines changed

.editorconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
end_of_line = lf
5+
insert_final_newline = true
6+
7+
[*.{js,json,sh,yml}]
8+
charset = utf-8
9+
indent_style = space
10+
indent_size = 2
11+
12+
[*.{py}]
13+
charset = utf-8
14+
indent_style = space
15+
indent_size = 4

.github/workflows/verify.yml

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,29 @@ on:
77
- main
88

99
jobs:
10+
check_types:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v5
14+
- name: Set up Python
15+
uses: actions/setup-python@v6
16+
with:
17+
python-version: '3'
18+
cache: 'pip'
19+
- name: Install Dependencies
20+
run: |
21+
pip install -r requirements.txt -r requirements_test.txt
22+
- name: Run mypy
23+
run: mypy --install-types --non-interactive libpyvivotek
1024
lint:
1125
runs-on: ubuntu-latest
1226
steps:
13-
- uses: actions/checkout@v2
27+
- uses: actions/checkout@v5
1428
- name: Set up Python
15-
uses: actions/setup-python@v2
29+
uses: actions/setup-python@v6
1630
with:
1731
python-version: '3'
32+
cache: 'pip'
1833
- name: Install Dependencies
1934
run: |
2035
pip install -r requirements.txt -r requirements_test.txt
@@ -27,22 +42,20 @@ jobs:
2742
os:
2843
- ubuntu
2944
python-version:
30-
- "3.8"
31-
- "3.9"
3245
- "3.10"
3346
- "3.11"
3447
- "3.12"
48+
- "3.13"
3549
runs-on: ${{ matrix.os }}-latest
3650
steps:
37-
- uses: actions/checkout@v2
38-
- name: Set up Python ${{ matrix.python-version }}
39-
uses: actions/setup-python@v2
51+
- uses: actions/checkout@v5
52+
- name: Set up Python
53+
uses: actions/setup-python@v6
4054
with:
41-
python-version: ${{ matrix.python-version }}
55+
python-version: '3'
56+
cache: 'pip'
4257
- name: Install Dependencies
4358
run: |
44-
pip install pytest
45-
pip install -r requirements.txt
46-
pip install -r requirements_test.txt
59+
pip install -r requirements.txt -r requirements_test.txt
4760
- name: Run tests
4861
run: pytest

libpyvivotek/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""A python implementation of the Vivotek IB8369A"""
22
from libpyvivotek.vivotek import VivotekCamera
33

4-
VERSION = "0.4.1"
4+
VERSION = "0.5.0"

libpyvivotek/vivotek.py

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""A python implementation of the Vivotek IB8369A"""
22
from textwrap import wrap
3+
from typing import Any
34

45
import requests
56
from requests.auth import HTTPBasicAuth
@@ -28,8 +29,17 @@ class VivotekCamera():
2829

2930
# pylint: disable=too-many-instance-attributes
3031
# pylint: disable=too-many-arguments
31-
def __init__(self, host, sec_lvl, port=None, usr=None, pwd=None, digest_auth=False, ssl=None,
32-
verify_ssl=True):
32+
# pylint: disable=too-many-positional-arguments
33+
def __init__(self,
34+
host: str,
35+
sec_lvl: str,
36+
port: int|None = None,
37+
usr: str = '',
38+
pwd: str = '',
39+
digest_auth: bool = False,
40+
ssl: bool|None = None,
41+
verify_ssl: bool = True
42+
) -> None:
3343
"""
3444
Initialize a camera.
3545
"""
@@ -53,7 +63,9 @@ def __init__(self, host, sec_lvl, port=None, usr=None, pwd=None, digest_auth=Fal
5363
if sec_lvl not in SECURITY_LEVELS:
5464
raise VivotekCameraError(f"Invalid security level: {sec_lvl}")
5565

56-
if usr is None or sec_lvl == 'anonymous':
66+
self._requests_auth: HTTPBasicAuth | HTTPDigestAuth | None
67+
68+
if usr == '' or sec_lvl == 'anonymous':
5769
self._requests_auth = None
5870
self._security_level = 'anonymous'
5971
else:
@@ -63,7 +75,7 @@ def __init__(self, host, sec_lvl, port=None, usr=None, pwd=None, digest_auth=Fal
6375
else:
6476
self._requests_auth = HTTPBasicAuth(usr, pwd)
6577

66-
self._model_name = None
78+
self._model_name: str | None = None
6779

6880
_protocol = 'https' if self._ssl else 'http'
6981
self._url_base = _protocol + "://" + self.host
@@ -75,12 +87,12 @@ def __init__(self, host, sec_lvl, port=None, usr=None, pwd=None, digest_auth=Fal
7587
self._set_param_url = self._cgi_url_base + API_PATHS["set"]
7688
self._still_image_url = self._url_base + CGI_BASE_PATH + "/viewer" + API_PATHS["still"]
7789

78-
def event_enabled(self, event_key):
90+
def event_enabled(self, event_key: str) -> bytes | Any | None:
7991
"""Return true if event for the provided key is enabled."""
8092
response = self.get_param(event_key)
8193
return int(response.replace("'", "")) == 1
8294

83-
def snapshot(self, quality=3):
95+
def snapshot(self, quality: int = 3) -> bytes | Any:
8496
"""Return the bytes of current still image."""
8597
try:
8698
response = requests.get(
@@ -95,31 +107,30 @@ def snapshot(self, quality=3):
95107
except requests.exceptions.RequestException as error:
96108
raise VivotekCameraError from error
97109

98-
def get_mac(self):
110+
def get_mac(self) -> str:
99111
"""Return the MAC address with colons"""
100112
return ":".join(wrap(self.get_serial(), 2))
101113

102-
def get_serial(self):
114+
def get_serial(self) -> str:
103115
"""Return the serial number which is also the MAC address."""
104116
return self.get_param('system_info_serialnumber')
105117

106-
def get_param(self, param):
118+
def get_param(self, param: str) -> str:
107119
"""Return the value of the provided key."""
108-
request_args = {
109-
"params": param,
110-
"timeout": 10,
111-
"verify": self.verify_ssl
112-
}
113-
if self._requests_auth is not None:
114-
request_args['auth'] = self._requests_auth
115120
try:
116-
response = requests.get(self._get_param_url, **request_args)
121+
response = requests.get(
122+
self._get_param_url,
123+
params=param,
124+
timeout=10,
125+
verify=self.verify_ssl,
126+
auth=self._requests_auth
127+
)
117128

118129
return self.__parse_response_value(response)
119130
except requests.exceptions.RequestException as error:
120131
raise VivotekCameraError from error
121132

122-
def set_param(self, param, value):
133+
def set_param(self, param: str, value: str | int | bool) -> str:
123134
"""Set and return the value of the provided key."""
124135
if SECURITY_LEVELS[self._security_level] < 4:
125136
raise VivotekCameraError(
@@ -139,7 +150,7 @@ def set_param(self, param, value):
139150
raise VivotekCameraError from error
140151

141152
@property
142-
def model_name(self):
153+
def model_name(self) -> str:
143154
"""Return the model name of the camera."""
144155
if self._model_name is not None:
145156
return self._model_name
@@ -148,7 +159,7 @@ def model_name(self):
148159
return self._model_name
149160

150161
@staticmethod
151-
def __parse_response_value(response):
162+
def __parse_response_value(response: requests.Response) -> str:
152163
"""
153164
Parse the response from an API call and return the value only.
154165
This assumes the response is in the key='value' format.

mypy.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[mypy]
2+
strict = True

py.typed

Whitespace-only changes.

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = [
99
]
1010
description = "Python Library for Vivotek IP Cameras"
1111
readme = "README.md"
12-
requires-python = ">=3.8"
12+
requires-python = ">=3.10"
1313
keywords = ["Camera", "IPC", "vivotek"]
1414
license = {text = "LGPLv3+"}
1515
classifiers = [
@@ -29,6 +29,8 @@ dynamic = ["version"]
2929
[project.optional-dependencies]
3030
tests = [
3131
"pylint>=2.3",
32+
"pytest",
33+
"mypy>=1.18.1",
3234
"vcrpy>=2.0"
3335
]
3436

requirements_test.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
mypy>=1.18
12
pylint>=2.3
3+
pytest>=7.1
24
vcrpy>=2.0

0 commit comments

Comments
 (0)