python-snap7 2.0.0__tar.gz → 2.0.1__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.
Files changed (35) hide show
  1. {python_snap7-2.0.0/python_snap7.egg-info → python_snap7-2.0.1}/PKG-INFO +4 -1
  2. {python_snap7-2.0.0 → python_snap7-2.0.1}/pyproject.toml +4 -3
  3. {python_snap7-2.0.0 → python_snap7-2.0.1/python_snap7.egg-info}/PKG-INFO +4 -1
  4. {python_snap7-2.0.0 → python_snap7-2.0.1}/python_snap7.egg-info/requires.txt +2 -0
  5. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/client.py +1 -7
  6. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/type.py +28 -1
  7. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/util/db.py +15 -3
  8. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/util/getters.py +9 -12
  9. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_client.py +12 -6
  10. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_util.py +16 -2
  11. {python_snap7-2.0.0 → python_snap7-2.0.1}/LICENSE +0 -0
  12. {python_snap7-2.0.0 → python_snap7-2.0.1}/MANIFEST.in +0 -0
  13. {python_snap7-2.0.0 → python_snap7-2.0.1}/README.rst +0 -0
  14. {python_snap7-2.0.0 → python_snap7-2.0.1}/python_snap7.egg-info/SOURCES.txt +0 -0
  15. {python_snap7-2.0.0 → python_snap7-2.0.1}/python_snap7.egg-info/dependency_links.txt +0 -0
  16. {python_snap7-2.0.0 → python_snap7-2.0.1}/python_snap7.egg-info/entry_points.txt +0 -0
  17. {python_snap7-2.0.0 → python_snap7-2.0.1}/python_snap7.egg-info/top_level.txt +0 -0
  18. {python_snap7-2.0.0 → python_snap7-2.0.1}/setup.cfg +0 -0
  19. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/__init__.py +0 -0
  20. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/common.py +0 -0
  21. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/error.py +0 -0
  22. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/logo.py +0 -0
  23. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/partner.py +0 -0
  24. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/protocol.py +0 -0
  25. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/protocol.pyi +0 -0
  26. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/py.typed +0 -0
  27. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/server/__init__.py +0 -0
  28. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/server/__main__.py +0 -0
  29. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/util/__init__.py +0 -0
  30. {python_snap7-2.0.0 → python_snap7-2.0.1}/snap7/util/setters.py +0 -0
  31. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_common.py +0 -0
  32. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_logo_client.py +0 -0
  33. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_mainloop.py +0 -0
  34. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_partner.py +0 -0
  35. {python_snap7-2.0.0 → python_snap7-2.0.1}/tests/test_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-snap7
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: Python wrapper for the snap7 library
5
5
  Author-email: Gijs Molenaar <gijsmolenaar@gmail.com>
6
6
  License: MIT License
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.9
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
21
22
  Requires-Python: >=3.9
22
23
  Description-Content-Type: text/x-rst
23
24
  License-File: LICENSE
@@ -28,12 +29,14 @@ Requires-Dist: types-setuptools; extra == "test"
28
29
  Requires-Dist: ruff; extra == "test"
29
30
  Requires-Dist: tox; extra == "test"
30
31
  Requires-Dist: types-click; extra == "test"
32
+ Requires-Dist: uv; extra == "test"
31
33
  Provides-Extra: cli
32
34
  Requires-Dist: rich; extra == "cli"
33
35
  Requires-Dist: click; extra == "cli"
34
36
  Provides-Extra: doc
35
37
  Requires-Dist: sphinx; extra == "doc"
36
38
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
39
+ Requires-Dist: enum-tools[sphinx]; extra == "doc"
37
40
 
38
41
  About
39
42
  =====
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-snap7"
7
- version = "2.0.0"
7
+ version = "2.0.1"
8
8
  description = "Python wrapper for the snap7 library"
9
9
  readme = "README.rst"
10
10
  authors = [
@@ -22,6 +22,7 @@ classifiers = [
22
22
  "Programming Language :: Python :: 3.10",
23
23
  "Programming Language :: Python :: 3.11",
24
24
  "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
25
26
  ]
26
27
  license = {text = "MIT License"}
27
28
  requires-python = ">=3.9"
@@ -32,9 +33,9 @@ Homepage = "https://github.com/gijzelaerr/python-snap7"
32
33
  Documentation = "https://python-snap7.readthedocs.io/en/latest/"
33
34
 
34
35
  [project.optional-dependencies]
35
- test = ["pytest", "mypy", "types-setuptools", "ruff", "tox", "types-click"]
36
+ test = ["pytest", "mypy", "types-setuptools", "ruff", "tox", "types-click", "uv"]
36
37
  cli = ["rich", "click" ]
37
- doc = ["sphinx", "sphinx_rtd_theme"]
38
+ doc = ["sphinx", "sphinx_rtd_theme", "enum-tools[sphinx]"]
38
39
 
39
40
  [tool.setuptools.package-data]
40
41
  snap7 = ["py.typed", "lib/libsnap7.so", "lib/snap7.dll", "lib/libsnap7.dylib"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-snap7
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: Python wrapper for the snap7 library
5
5
  Author-email: Gijs Molenaar <gijsmolenaar@gmail.com>
6
6
  License: MIT License
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.9
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
21
22
  Requires-Python: >=3.9
22
23
  Description-Content-Type: text/x-rst
23
24
  License-File: LICENSE
@@ -28,12 +29,14 @@ Requires-Dist: types-setuptools; extra == "test"
28
29
  Requires-Dist: ruff; extra == "test"
29
30
  Requires-Dist: tox; extra == "test"
30
31
  Requires-Dist: types-click; extra == "test"
32
+ Requires-Dist: uv; extra == "test"
31
33
  Provides-Extra: cli
32
34
  Requires-Dist: rich; extra == "cli"
33
35
  Requires-Dist: click; extra == "cli"
34
36
  Provides-Extra: doc
35
37
  Requires-Dist: sphinx; extra == "doc"
36
38
  Requires-Dist: sphinx_rtd_theme; extra == "doc"
39
+ Requires-Dist: enum-tools[sphinx]; extra == "doc"
37
40
 
38
41
  About
39
42
  =====
@@ -6,6 +6,7 @@ click
6
6
  [doc]
7
7
  sphinx
8
8
  sphinx_rtd_theme
9
+ enum-tools[sphinx]
9
10
 
10
11
  [test]
11
12
  pytest
@@ -14,3 +15,4 @@ types-setuptools
14
15
  ruff
15
16
  tox
16
17
  types-click
18
+ uv
@@ -469,9 +469,6 @@ class Client:
469
469
 
470
470
  Returns:
471
471
  If size is 0, it returns a 0, otherwise an `Array` of specified block type.
472
-
473
- Raises:
474
- :obj:`ValueError`: if the `block_type` is not valid.
475
472
  """
476
473
 
477
474
  logger.debug(f"listing blocks of type: {block_type} size: {size}")
@@ -498,11 +495,8 @@ class Client:
498
495
  Returns:
499
496
  Structure of information from block.
500
497
 
501
- Raises:
502
- :obj:`ValueError`: if the `blocktype` is not valid.
503
-
504
498
  Examples:
505
- >>> block_info = Client().get_block_info("DB", 1)
499
+ >>> block_info = Client().get_block_info(block_type.DB, 1)
506
500
  >>> print(block_info)
507
501
  Block type: 10
508
502
  Block number: 1
@@ -45,7 +45,10 @@ mkLog = 1
45
45
 
46
46
 
47
47
  class Parameter(IntEnum):
48
- # // PARAMS LIST
48
+ """
49
+ The snap7 parameter types
50
+ """
51
+
49
52
  LocalPort = 1
50
53
  RemotePort = 2
51
54
  PingTimeout = 3
@@ -87,6 +90,10 @@ class Parameter(IntEnum):
87
90
  # Area ID
88
91
  # Word Length
89
92
  class WordLen(IntEnum):
93
+ """
94
+ The snap7 word length types
95
+ """
96
+
90
97
  Bit = 0x01
91
98
  Byte = 0x02
92
99
  Char = 0x03
@@ -113,6 +120,10 @@ class WordLen(IntEnum):
113
120
 
114
121
 
115
122
  class Area(IntEnum):
123
+ """
124
+ The snap7 area types
125
+ """
126
+
116
127
  PE = 0x81
117
128
  PA = 0x82
118
129
  MK = 0x83
@@ -134,6 +145,8 @@ Areas = Area
134
145
 
135
146
  class SrvArea(IntEnum):
136
147
  """
148
+ The snap7 server area types
149
+
137
150
  NOTE: these values are DIFFERENT from the normal area IDs.
138
151
  """
139
152
 
@@ -146,6 +159,10 @@ class SrvArea(IntEnum):
146
159
 
147
160
 
148
161
  class Block(IntEnum):
162
+ """
163
+ The snap7 block type
164
+ """
165
+
149
166
  OB = 0x38
150
167
  DB = 0x41
151
168
  SDB = 0x42
@@ -173,6 +190,10 @@ cpu_statuses = {
173
190
 
174
191
 
175
192
  class SrvEvent(Structure):
193
+ """
194
+ The snap7 server event structure
195
+ """
196
+
176
197
  _fields_ = [
177
198
  ("EvtTime", time_t),
178
199
  ("EvtSender", c_int),
@@ -193,6 +214,10 @@ class SrvEvent(Structure):
193
214
 
194
215
 
195
216
  class BlocksList(Structure):
217
+ """
218
+ The snap7 block list structure
219
+ """
220
+
196
221
  _fields_ = [
197
222
  ("OBCount", c_int32),
198
223
  ("FBCount", c_int32),
@@ -250,6 +275,8 @@ class TS7BlockInfo(Structure):
250
275
 
251
276
 
252
277
  class S7DataItem(Structure):
278
+ """ """
279
+
253
280
  _pack_ = 1
254
281
  _fields_ = [
255
282
  ("Area", c_int32),
@@ -149,11 +149,23 @@ def parse_specification(db_specification: str) -> Dict[str, Any]:
149
149
  Parsed DB specification.
150
150
  """
151
151
  parsed_db_specification = {}
152
+ pattern = r"""
153
+ (?P<index>\d+(\.\d+)?)\s+ # Match integer or decimal index
154
+ (?P<var_name>.*?)\s+ # Non-greedy match for variable name
155
+ (?P<_type>\S+)$ # Match type at end of line
156
+ """
157
+ regex = re.compile(pattern, re.VERBOSE)
152
158
 
153
159
  for line in db_specification.split("\n"):
154
160
  if line and not line.lstrip().startswith("#"):
155
- index, var_name, _type = line.lstrip().split("#")[0].split()
156
- parsed_db_specification[var_name] = (index, _type)
161
+ match = regex.match(line.strip())
162
+ if match:
163
+ index = match.group("index")
164
+ var_name = match.group("var_name")
165
+ _type = match.group("_type")
166
+ var_name = var_name.strip()
167
+
168
+ parsed_db_specification[var_name] = (index, _type)
157
169
 
158
170
  return parsed_db_specification
159
171
 
@@ -303,7 +315,7 @@ class DB:
303
315
  if key and key in self.index:
304
316
  msg = f"{key} not unique!"
305
317
  logger.error(msg)
306
- self.index[key] = row
318
+ self.index[str(key)] = row
307
319
 
308
320
  def __getitem__(self, key: str, default: Optional[None] = None) -> Union[None, "Row"]:
309
321
  """Access a row of the table through its index.
@@ -493,25 +493,24 @@ def get_lint(bytearray_: bytearray, byte_index: int) -> int:
493
493
  def get_lreal(bytearray_: bytearray, byte_index: int) -> float:
494
494
  """Get the long real
495
495
 
496
- Notes:
497
- Datatype `lreal` (long real) consists in 8 bytes in the PLC.
498
- Negative Range: -1.7976931348623158e+308 to -2.2250738585072014e-308
499
- Positive Range: +2.2250738585072014e-308 to +1.7976931348623158e+308
500
- Zero: ±0
496
+ Datatype `lreal` (long real) consists in 8 bytes in the PLC.
497
+ Negative Range: -1.7976931348623158e+308 to -2.2250738585072014e-308
498
+ Positive Range: +2.2250738585072014e-308 to +1.7976931348623158e+308
499
+ Zero: ±0
501
500
 
502
501
  Args:
503
502
  bytearray_: buffer to read from.
504
503
  byte_index: byte index from where to start reading.
505
504
 
506
505
  Returns:
507
- Value read.
506
+ The real value.
508
507
 
509
508
  Examples:
510
509
  read lreal value (here as example 12345.12345) from DB1.10 of a PLC
511
510
  >>> from snap7 import Client
512
511
  >>> data = Client().db_read(db_number=1, start=10, size=8)
513
512
  >>> get_lreal(data, 0)
514
- 12345.12345
513
+ 12345.12345
515
514
  """
516
515
  return float(struct.unpack_from(">d", bytearray_, offset=byte_index)[0])
517
516
 
@@ -637,7 +636,7 @@ def get_char(bytearray_: bytearray, byte_index: int) -> str:
637
636
  >>> from snap7 import Client
638
637
  >>> data = Client().db_read(db_number=1, start=10, size=1)
639
638
  >>> get_char(data, 0)
640
- 'C'
639
+ 'C'
641
640
  """
642
641
  char = chr(bytearray_[byte_index])
643
642
  return char
@@ -646,9 +645,7 @@ def get_char(bytearray_: bytearray, byte_index: int) -> str:
646
645
  def get_wchar(bytearray_: bytearray, byte_index: int) -> str:
647
646
  """Get wchar value from bytearray.
648
647
 
649
- Notes:
650
- Datatype `wchar` in the PLC is represented in 2 bytes. It has to be in utf-16-be format.
651
-
648
+ Datatype `wchar` in the PLC is represented in 2 bytes. It has to be in utf-16-be format.
652
649
 
653
650
  Args:
654
651
  bytearray_: buffer to read from.
@@ -662,7 +659,7 @@ def get_wchar(bytearray_: bytearray, byte_index: int) -> str:
662
659
  >>> from snap7 import Client
663
660
  >>> data = Client().db_read(db_number=1, start=10, size=2)
664
661
  >>> get_wchar(data, 0)
665
- 'C'
662
+ 'C'
666
663
  """
667
664
  if bytearray_[byte_index] == 0:
668
665
  return chr(bytearray_[1])
@@ -18,7 +18,7 @@ from ctypes import (
18
18
  pointer,
19
19
  Array,
20
20
  )
21
- from datetime import datetime, timedelta, date
21
+ from datetime import datetime, timedelta, timezone
22
22
  from multiprocessing import Process
23
23
  from unittest import mock
24
24
  from typing import cast as typing_cast
@@ -382,8 +382,8 @@ class TestClient(unittest.TestCase):
382
382
  self.assertRaises(Exception, self.client.get_param, non_client)
383
383
 
384
384
  def test_as_copy_ram_to_rom(self) -> None:
385
- response = self.client.as_copy_ram_to_rom(timeout=1)
386
- self.client.wait_as_completion(1100)
385
+ response = self.client.as_copy_ram_to_rom(timeout=2)
386
+ self.client.wait_as_completion(2000)
387
387
  self.assertEqual(0, response)
388
388
 
389
389
  def test_as_ct_read(self) -> None:
@@ -714,7 +714,7 @@ class TestClient(unittest.TestCase):
714
714
 
715
715
  def test_copy_ram_to_rom(self) -> None:
716
716
  # Cli_CopyRamToRom
717
- self.assertEqual(0, self.client.copy_ram_to_rom(timeout=1))
717
+ self.assertEqual(0, self.client.copy_ram_to_rom(timeout=2))
718
718
 
719
719
  def test_ct_read(self) -> None:
720
720
  # Cli_CTRead
@@ -803,8 +803,14 @@ class TestClient(unittest.TestCase):
803
803
  self.assertEqual(10, block_info.BlkType)
804
804
  self.assertEqual(99, block_info.BlkNumber)
805
805
  self.assertEqual(2752512, block_info.SBBLength)
806
- self.assertEqual(bytes((date(2019, 6, 27).strftime("%Y/%m/%d")), encoding="utf-8"), block_info.CodeDate)
807
- self.assertEqual(bytes((date(2019, 6, 27).strftime("%Y/%m/%d")), encoding="utf-8"), block_info.IntfDate)
806
+ self.assertEqual(
807
+ bytes((datetime(2019, 6, 27, tzinfo=timezone.utc).astimezone().strftime("%Y/%m/%d")), encoding="utf-8"),
808
+ block_info.CodeDate,
809
+ )
810
+ self.assertEqual(
811
+ bytes((datetime(2019, 6, 27, tzinfo=timezone.utc).astimezone().strftime("%Y/%m/%d")), encoding="utf-8"),
812
+ block_info.IntfDate,
813
+ )
808
814
 
809
815
  def test_iso_exchange_buffer(self) -> None:
810
816
  # Cli_IsoExchangeBuffer
@@ -53,7 +53,7 @@ test_spec_indented = """
53
53
  12.0 testbool1 BOOL
54
54
  12.1 testbool2 BOOL
55
55
  12.2 testbool3 BOOL
56
- # 12.3 testbool4 BOOL
56
+ # 12.3 test bool4 BOOL
57
57
  # 12.4 testbool5 BOOL
58
58
  # 12.5 testbool6 BOOL
59
59
  # 12.6 testbool7 BOOL
@@ -439,13 +439,27 @@ class TestS7util(unittest.TestCase):
439
439
  self.assertEqual(row["testbool2"], 1)
440
440
  self.assertEqual(row["testbool3"], 1)
441
441
  self.assertEqual(row["testbool4"], 1)
442
-
443
442
  self.assertEqual(row["testbool5"], 0)
444
443
  self.assertEqual(row["testbool6"], 0)
445
444
  self.assertEqual(row["testbool7"], 0)
446
445
  self.assertEqual(row["testbool8"], 0)
447
446
  self.assertEqual(row["NAME"], "test")
448
447
 
448
+ def test_db_creation_vars_with_whitespace(self) -> None:
449
+ test_array = bytearray(_bytearray * 1)
450
+ test_spec = """
451
+ 50 testZeroSpaces BYTE
452
+ 52 testOne Space BYTE
453
+ 59 testTWo Spaces BYTE
454
+ """
455
+
456
+ test_db = DB(1, test_array, test_spec, row_size=len(_bytearray), size=1, layout_offset=0, db_offset=0)
457
+ db_export = test_db.export()
458
+ for i in db_export:
459
+ self.assertTrue("testZeroSpaces" in db_export[i].keys())
460
+ self.assertTrue("testOne Space" in db_export[i].keys())
461
+ self.assertTrue("testTWo Spaces" in db_export[i].keys())
462
+
449
463
  def test_db_export(self) -> None:
450
464
  test_array = bytearray(_bytearray * 10)
451
465
  test_db = DB(1, test_array, test_spec, row_size=len(_bytearray), size=10, layout_offset=4, db_offset=0)
File without changes
File without changes
File without changes
File without changes
File without changes