Skip to content

Commit 4bd1191

Browse files
committed
documentation
1 parent 2789d12 commit 4bd1191

31 files changed

Lines changed: 327 additions & 182 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ nosetests.xml
3939
/include
4040
.mypy_cache
4141
pyvenv.cfg
42+
site/

.readthedocs.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# .readthedocs.yaml
2+
version: 2
3+
mkdocs:
4+
configuration: mkdocs.yml
5+
python:
6+
install:
7+
- requirements: requirements-dev.txt

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2013-2017 Andrea Bonomi <andrea.bonomi@gmail.com>
3+
Copyright (c) 2013-2022 Andrea Bonomi <andrea.bonomi@gmail.com>
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy of
66
this software and associated documentation files (the "Software"), to deal in

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ coverage:
2323

2424
.PHONY: docs
2525
docs:
26-
cd docs; $(MAKE) html
26+
@mkdocs build
27+
@mkdocs gh-deploy
2728

2829
lint:
2930
flake8 cstruct tests

cstruct/__init__.py

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
#
42
# Copyright (c) 2013-2019 Andrea Bonomi <andrea.bonomi@gmail.com>
53
#
64
# Published under the terms of the MIT license.
@@ -40,7 +38,6 @@
4038
TYPEDEFS,
4139
C_TYPE_TO_FORMAT,
4240
CHAR_ZERO,
43-
EMPTY_BYTES_STRING,
4441
)
4542
from .abstract import CStructMeta, AbstractCStruct
4643
from .cstruct import CStruct
@@ -52,7 +49,6 @@
5249
'BIG_ENDIAN',
5350
'NATIVE_ORDER',
5451
'CHAR_ZERO',
55-
'EMPTY_BYTES_STRING',
5652
'CStruct',
5753
'MemCStruct',
5854
'define',
@@ -68,8 +64,9 @@ def define(key: str, value: Any) -> None:
6864
"""
6965
Define a constant that can be used in the C struct
7066
71-
:param key: identifier
72-
:param value: value of the constant
67+
Args:
68+
key: identifier
69+
value: value of the constant
7370
"""
7471
DEFINES[key] = value
7572

@@ -78,7 +75,8 @@ def undef(key: str) -> None:
7875
"""
7976
Undefine a symbol that was previously defined with define
8077
81-
:param key: identifier
78+
Args:
79+
key: identifier
8280
"""
8381
del DEFINES[key]
8482

@@ -87,7 +85,8 @@ def getdef(key: str) -> Any:
8785
"""
8886
Return the value for a constant
8987
90-
:param key: identifier
88+
Args:
89+
key: identifier
9190
"""
9291
return DEFINES[key]
9392

@@ -96,8 +95,9 @@ def typedef(type_: str, alias: str) -> None:
9695
"""
9796
Define an alias name for a data type
9897
99-
:param type_: data type
100-
:param alias: new alias name
98+
Args:
99+
type_: data type
100+
alias: new alias name
101101
"""
102102
TYPEDEFS[alias] = type_
103103

@@ -106,8 +106,11 @@ def sizeof(type_: str) -> int:
106106
"""
107107
Return the size of the type.
108108
109-
:param type_: C type, struct or union (e.g. 'short int' or 'struct ZYZ')
110-
:return: size in bytes
109+
Args:
110+
type_: C type, struct or union (e.g. 'short int' or 'struct ZYZ')
111+
112+
Returns:
113+
size: size in bytes
111114
"""
112115
while type_ in TYPEDEFS:
113116
type_ = TYPEDEFS[type_]
@@ -129,18 +132,24 @@ def sizeof(type_: str) -> int:
129132

130133

131134
def parse(
132-
__struct__: str, __cls__: Optional[Type[AbstractCStruct]] = None, __name__: Optional[str] = None, **kargs: Dict[str, Any]
135+
__struct__: str,
136+
__cls__: Optional[Type[AbstractCStruct]] = None,
137+
__name__: Optional[str] = None,
138+
**kargs: Dict[str, Any]
133139
) -> Optional[Type[AbstractCStruct]]:
134140
"""
135141
Return a new class mapping a C struct/union definition.
136142
If the string does not contains any definition, return None.
137143
138-
:param __struct__: definition of the struct (or union) in C syntax
139-
:param __cls__: (optional) super class - CStruct(default) or MemCStruct
140-
:param __name__: (optional) name of the new class. If empty, a name based on the __struct__ hash is generated
141-
:param __byte_order__: (optional) byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
142-
:param __is_union__: (optional) True for union, False for struct (default)
143-
:returns: __cls__ subclass
144+
Args:
145+
__struct__ (str): definition of the struct (or union) in C syntax
146+
__cls__ (type): super class - CStruct(default) or MemCStruct
147+
__name__ (str): name of the new class. If empty, a name based on the __struct__ hash is generated
148+
__byte_order__ (str): byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
149+
__is_union__ (bool): True for union, False for struct (default)
150+
151+
Returns:
152+
cls: __cls__ subclass
144153
"""
145154
if __cls__ is None:
146155
__cls__ = CStruct

cstruct/abstract.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
from abc import ABCMeta
2828
from collections import OrderedDict
2929
from typing import Any, BinaryIO, List, Dict, Optional, Type, Tuple, Union
30-
from .base import STRUCTS
3130
import hashlib
31+
from .base import STRUCTS
3232
from .c_parser import parse_struct, parse_def, Tokens
3333
from .field import calculate_padding, FieldType
34+
from .exceptions import CStructException
3435

3536
__all__ = ['CStructMeta', 'AbstractCStruct']
3637

3738

3839
class CStructMeta(ABCMeta):
3940
__size__: int = 0
4041

41-
# def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> MetaClass:
4242
def __new__(metacls: Type[type], name: str, bases: Tuple[str], namespace: Dict[str, Any]) -> Type[Any]:
4343
__struct__ = namespace.get('__struct__', None)
4444
namespace['__cls__'] = bases[0] if bases else None
@@ -58,6 +58,7 @@ def __new__(metacls: Type[type], name: str, bases: Tuple[str], namespace: Dict[s
5858
return new_class
5959

6060
def __len__(cls) -> int:
61+
"Structure size (in bytes)"
6162
return cls.__size__
6263

6364
@property
@@ -67,12 +68,22 @@ def size(cls) -> int:
6768

6869

6970
class AbstractCStruct(metaclass=CStructMeta):
71+
"""
72+
Abstract C struct to Python class
73+
"""
74+
7075
__size__: int = 0
76+
" Size in bytes "
7177
__fields__: List[str] = []
78+
" Struct/union fileds "
7279
__fields_types__: Dict[str, FieldType]
80+
" Dictionary mapping field names to types "
7381
__byte_order__: Optional[str] = None
82+
" Byte order "
7483
__alignment__: int = 0
84+
" Alignament "
7585
__is_union__: bool = False
86+
" True if the class is an union, False if it is a struct "
7687

7788
def __init__(
7889
self, buffer: Optional[Union[bytes, BinaryIO]] = None, flexible_array_length: Optional[int] = None, **kargs: Dict[str, Any]
@@ -92,18 +103,30 @@ def __init__(
92103

93104
@classmethod
94105
def parse(
95-
cls, __struct__: Union[str, Tokens, Dict[str, Any]], __name__: Optional[str] = None, **kargs: Dict[str, Any]
106+
cls,
107+
__struct__: Union[str, Tokens, Dict[str, Any]],
108+
__name__: Optional[str] = None,
109+
__byte_order__: Optional[str] = None,
110+
__is_union__: Optional[bool] = False,
111+
**kargs: Dict[str, Any]
96112
) -> Type["AbstractCStruct"]:
97113
"""
98114
Return a new class mapping a C struct/union definition.
99115
100-
:param __struct__: definition of the struct (or union) in C syntax
101-
:param __name__: (optional) name of the new class. If empty, a name based on the __struct__ hash is generated
102-
:param __byte_order__: (optional) byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
103-
:param __is_union__: (optional) True for union, False for struct (default)
104-
:returns: cls subclass
116+
Args:
117+
__struct__: definition of the struct (or union) in C syntax
118+
__name__: name of the new class. If empty, a name based on the __struct__ hash is generated
119+
__byte_order__: byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
120+
__is_union__: True for union, False for struct
121+
122+
Returns:
123+
cls: a new class mapping the defintion
105124
"""
106125
cls_kargs: Dict[str, Any] = dict(kargs)
126+
if __byte_order__ is not None:
127+
cls_kargs['__byte_order__'] = __byte_order__
128+
if __is_union__ is not None:
129+
cls_kargs['__is_union__'] = __is_union__
107130
cls_kargs['__struct__'] = __struct__
108131
if isinstance(__struct__, (str, Tokens)):
109132
del cls_kargs['__struct__']
@@ -123,20 +146,23 @@ def set_flexible_array_length(self, flexible_array_length: Optional[int]) -> Non
123146
"""
124147
Set flexible array length (i.e. number of elements)
125148
126-
:flexible_array_length: flexible array length
149+
Args:
150+
flexible_array_length: flexible array length
127151
"""
128152
if flexible_array_length is not None:
129153
# Search for the flexible array
130154
flexible_array: Optional[FieldType] = [x for x in self.__fields_types__.values() if x.flexible_array][0]
131155
if flexible_array is None:
132-
raise ValueError("Flexible array not found in struct")
156+
raise CStructException("Flexible array not found in struct")
133157
flexible_array.vlen = flexible_array_length
134158

135159
def unpack(self, buffer: Optional[Union[bytes, BinaryIO]], flexible_array_length: Optional[int] = None) -> bool:
136160
"""
137161
Unpack bytes containing packed C structure data
138162
139-
:param buffer: bytes or binary stream to be unpacked
163+
Args:
164+
buffer: bytes or binary stream to be unpacked
165+
flexible_array_length: flexible array length
140166
"""
141167
self.set_flexible_array_length(flexible_array_length)
142168
if hasattr(buffer, 'read'):
@@ -151,8 +177,10 @@ def unpack_from(
151177
"""
152178
Unpack bytes containing packed C structure data
153179
154-
:param buffer: bytes to be unpacked
155-
:param offset: optional buffer offset
180+
Args:
181+
buffer: bytes to be unpacked
182+
offset: optional buffer offset
183+
flexible_array_length: flexible array length
156184
"""
157185
raise NotImplementedError
158186

cstruct/base.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
#
42
# Copyright (c) 2013-2019 Andrea Bonomi <andrea.bonomi@gmail.com>
53
#
64
# Published under the terms of the MIT license.
@@ -38,16 +36,15 @@
3836
'DEFINES',
3937
'TYPEDEFS',
4038
'C_TYPE_TO_FORMAT',
41-
'EMPTY_BYTES_STRING',
4239
'CHAR_ZERO',
4340
]
4441

45-
# little-endian, std. size & alignment
4642
LITTLE_ENDIAN = '<'
47-
# big-endian, std. size & alignment
43+
"Little-endian, std. size & alignment"
4844
BIG_ENDIAN = '>'
49-
# native order, size & alignment
45+
"Big-endian, std. size & alignment"
5046
NATIVE_ORDER = '@'
47+
"Native order, size & alignment"
5148

5249
STRUCTS: Dict[str, Type["AbstractCStruct"]] = {}
5350

@@ -94,5 +91,4 @@
9491
'uint64': 'Q',
9592
}
9693

97-
EMPTY_BYTES_STRING = bytes()
9894
CHAR_ZERO = bytes('\0', 'ascii')

cstruct/c_expr.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
#
42
# Copyright (c) 2013-2019 Andrea Bonomi <andrea.bonomi@gmail.com>
53
#
64
# Published under the terms of the MIT license.
@@ -28,6 +26,7 @@
2826
import operator
2927
from typing import Any, Callable, Dict, Union, Type, TYPE_CHECKING
3028
from .base import DEFINES, STRUCTS
29+
from .exceptions import EvalError
3130

3231
if TYPE_CHECKING:
3332
from .abstract import AbstractCStruct
@@ -36,9 +35,31 @@
3635

3736

3837
def c_eval(expr: str) -> Union[int, float]:
39-
"Evaluate a C arithmetic/logic expression and return the result"
40-
expr = expr.replace("!", " not ").replace("&&", " and ").replace("||", " or ")
41-
return eval_node(ast.parse(expr.strip()).body[0])
38+
"""
39+
Evaluate a C arithmetic/logic expression and return the result
40+
41+
Examples:
42+
>>> c_eval('10 + (5 / 3)')
43+
11
44+
>>> c_eval('!0')
45+
1
46+
>>> c_eval('sizeof(x)')
47+
128
48+
49+
Args:
50+
expr: C expression
51+
52+
Returns:
53+
result: the expression evaluation result
54+
55+
Raises:
56+
EvalError: expression evaluation error
57+
"""
58+
try:
59+
expr = expr.replace("!", " not ").replace("&&", " and ").replace("||", " or ")
60+
return eval_node(ast.parse(expr.strip()).body[0])
61+
except Exception:
62+
raise EvalError
4263

4364

4465
def eval_node(node: ast.stmt) -> Union[int, float]:
@@ -79,9 +100,9 @@ def eval_div(node) -> Union[int, float]:
79100

80101

81102
def eval_call(node) -> Union[int, float]:
82-
if node.func.id == "sizeof":
83-
from . import sizeof
103+
from . import sizeof
84104

105+
if node.func.id == "sizeof":
85106
args = [eval_node(x) for x in node.args]
86107
return sizeof(*args)
87108
raise KeyError(node.func.id)

0 commit comments

Comments
 (0)