Commit 286b8e3d authored by Dan Gass's avatar Dan Gass
Browse files

alpha 23

parent bfcb63b8
......@@ -21,6 +21,7 @@ Plum Types
:mod:`plum.int.bitfields` integer with bit field accessors
:mod:`plum.int.enum` integer enumerated constants
:mod:`plum.int.flag` integer with bit flags
:mod:`plum.ipv4` IPV4 address
:mod:`plum.nil` no bytes
:mod:`plum.str` string
:mod:`plum.structure` structure of uniquely typed
......@@ -42,6 +43,10 @@ Plum Types
int.bitfields <types/int_bitfields.rst>
int.enum <types/int_enum.rst>
int.flag <types/int_flag.rst>
ipv4 <types/ipv4.rst>
ipv4.big <types/ipv4_big.rst>
ipv4.little <types/ipv4_little.rst>
ipv4.native <types/ipv4_native.rst>
nil <types/nil.rst>
str <types/str.rst>
structure <types/structure.rst>
......
############################
[plum.ipv4] Module Reference
############################
.. include:: ../../alias.txt
.. automodule:: plum.ipv4
.. autoclass:: IpV4Address
.. autoclass:: IpV4AddressType
################################
[plum.ipv4.big] Module Reference
################################
.. include:: ../../alias.txt
.. automodule:: plum.ipv4.big
.. autoclass:: IpV4Address
###################################
[plum.ipv4.little] Module Reference
###################################
.. include:: ../../alias.txt
.. automodule:: plum.ipv4.little
.. autoclass:: IpV4Address
###################################
[plum.ipv4.native] Module Reference
###################################
.. include:: ../../alias.txt
.. automodule:: plum.ipv4.native
.. autoclass:: IpV4Address
......@@ -8,6 +8,9 @@ Versions increment per `semver <http://semver.org/>`_ (except for alpha series).
Alpha (0.1.0aX) Versions (only serial number X increments)
**********************************************************
+ 0.1.0a23 (2020-Mar-08)
- Add IPV4 address support (not class name changed).
+ 0.1.0a22 (2020-Mar-05)
- Add preliminary IPV4 address support (docs and test not yet complete).
......
......@@ -12,6 +12,7 @@
plum.int.bitfields <int_bitfields>
plum.int.enum <int_enum>
plum.int.flag <int_flag>
plum.ipv4 <ipv4address>
plum.nil <nil>
plum.str <str/index>
plum.structure <structure/index>
#######################################
[plum.ipv4] Tutorial: IPV4 Address Type
#######################################
.. include:: ../../alias.txt
This tutorial teaches how to use the plum type classes provided by the :mod:`plum.ipv4`
subpackage for packing and unpacking IPV4 addresses.
.. contents::
:local:
***********************
IPV4 Address Byte Order
***********************
The :mod:`plum.ipv4` subpackage includes three modules, one for each endian
byte order: :mod:`plum.ipv4.big`, :mod:`plum.ipv4.little`, and :mod:`plum.ipv4.native`
(native byte order follows host architecture). Each module provides a plum type class
for IPV4 addresses: :class:`~plum.ipv4.little.IpV4Address`. This tutorial demonstrates
the big endian variant. The little endian variant has identical features except
reversed byte order.
***************
Unpacking Bytes
***************
The plum IPV4 address types convert bytes into Python :class:`IpV4Address` instances when used
with the various plum unpacking mechanisms. For example:
>>> from plum import unpack, Buffer
>>> from plum.ipv4.big import IpV4Address
>>>
>>> # utility function
>>> unpack(IpV4Address, b'\x01\x02\x03\x04')
IpV4Address('1.2.3.4')
>>>
>>> # class method
>>> IpV4Address.unpack(b'\x01\x02\x03\x04')
IpV4Address('1.2.3.4')
>>>
>>> # bytes buffer
>>> with Buffer(b'\x01\x02\x03\x04') as buffer:
... address = buffer.unpack(IpV4Address)
...
>>> address
IpV4Address('1.2.3.4')
*************
Packing Bytes
*************
The plum IPV4 address types convert strings, integers, or iterables into bytes
when used with the various plum packing mechanisms. For example:
>>> from plum import pack
>>> from plum.ipv4.big import IpV4Address
>>>
>>> # utility function (packing a string)
>>> pack(IpV4Address, '1.2.3.4')
bytearray(b'\x01\x02\x03\x04')
>>>
>>> # class method (packing an integer)
>>> IpV4Address.pack(0x1020304)
bytearray(b'\x01\x02\x03\x04')
>>>
>>> # instance method (with iterable)
>>> IpV4Address([1, 2, 3, 4]).pack()
bytearray(b'\x01\x02\x03\x04')
*************************************
Instantiation and Instance Properties
*************************************
|plum| IPV4 address constructors accept strings, integers, or iterables.
The representation of the instance includes the class name to
differentiate it from a standard Python string:
>>> from plum.ipv4.big import IpV4Address
>>>
>>> # string
>>> IpV4Address('1.2.3.4')
IpV4Address('1.2.3.4')
>>>
>>> # integer
>>> IpV4Address(0x1020304)
IpV4Address('1.2.3.4')
>>>
>>> # iterable
>>> IpV4Address([1, 2, 3, 4])
IpV4Address('1.2.3.4')
Instances support the :meth:`pack` method:
>>> address = IpV4Address('1.2.3.4')
>>> address.pack()
bytearray(b'\x01\x02\x03\x04')
Instances support bit mask operations:
>>> address = IpV4Address('1.2.3.4')
>>> address & 0xffffff00
IpV4Address('1.2.3.0')
>>>
>>> address |= 0xff
>>> address
IpV4Address('1.2.3.255')
Instances support conversions to ``int``, ``float``, and ``bool``:
>>> address = IpV4Address('1.2.3.4')
>>> int(address)
16909060
>>>
>>> float(address)
16909060.0
>>>
>>> bool(address)
True
Instances support iterating the octets and access via indexing:
>>> address = IpV4Address('1.2.3.4')
>>>
>>> list(address)
[1, 2, 3, 4]
>>>
>>> len(address)
4
>>>
>>> address[3] = 99
>>> address
IpV4Address('1.2.3.99')
......@@ -7,4 +7,4 @@ omit =
*/plum/int/native.py
*/plum/int/enum/native.py
*/plum/int/flag/native.py
*/plum/ipv4/native.py
......@@ -36,7 +36,7 @@ class VersionInfo:
major = 0
minor = 1
micro = 0
serial = 22
serial = 23
version = f'{major}.{minor}.{micro}'
......
......@@ -383,7 +383,7 @@ def getbytes(buffer, offset, nbytes, dump, cls):
else:
dump.memory = chunk
cls_name = '' if cls is None else f'{cls.__name__} '
cls_name = '' if not cls else f'{cls.__name__} '
unpack_shortage = (
f'{nbytes - len(chunk)} too few bytes to unpack {cls_name}'
......
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Copyright 2020 Daniel Mark Gass, see __about__.py for license information.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Interpret bytes as IPV4 address."""
from ._ipv4address import IpV4Address
from ._ipv4addresstype import IpV4AddressType
......@@ -4,15 +4,21 @@
"""Interpret bytes as IPV4 address."""
from .._plum import Plum, getbytes
from ._ipv4type import IpV4Type
from ._ipv4addresstype import IpV4AddressType
class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
class IpV4Address(Plum, metaclass=IpV4AddressType, byteorder='little'):
"""IPV4 address, little endian format."""
"""IPV4 address, little endian format.
:param address: IP address
:type address: str or int or iterable
"""
# filled in by metaclass
__byteorder__ = 'little'
__little_endian__ = True
__nbytes__ = 4
def __init__(self, address):
......@@ -23,26 +29,29 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
# TypeError -> bytes like
# AttributeError -> not a string or bytes like
try:
# assume iterable (e.g. bytearray, list, IpV4, etc)
# assume iterable (e.g. bytearray, list, IpV4Address, etc)
octets = list(address)
except TypeError:
try:
# assume integer
octets = address.to_bytes(4, self.__byteorder__, signed=False)
except AttributeError:
# force error, unknown type
octets = []
raise TypeError(f'invalid IPV4 address: {address!r}')
else:
if self.__little_endian__:
octets.reverse()
if len(octets) != 4:
raise ValueError(f'invalid IPV4 address: {address!r}')
self.__buffer__ = bytearray(int(octet) for octet in octets)
self.__buffer__ = bytes(int(octet) for octet in octets)
@classmethod
def __unpack__(cls, buffer, offset, parents, dump):
chunk, offset = getbytes(buffer, offset, 4, dump, cls)
octets, offset = getbytes(buffer, offset, 4, dump, cls)
address = cls(chunk)
address = object.__new__(cls)
address.__buffer__ = bytes(octets)
if dump:
dump.value = str(address)
......@@ -68,7 +77,11 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
return end
def __str__(self):
return '.'.join(str(octet) for octet in self.__buffer__)
if self.__little_endian__:
address = '.'.join(str(octet) for octet in reversed(self.__buffer__))
else:
address = '.'.join(str(octet) for octet in self.__buffer__)
return address
def __repr__(self):
return f'{type(self).__name__}({str(self)!r})'
......@@ -105,36 +118,6 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
def __bool__(self):
return bool(int(self))
def __add__(self, other):
return int(self).__add__(self._convert_to_int(other))
def __sub__(self, other):
return int(self).__sub__(self._convert_to_int(other))
def __mul__(self, other):
return int(self).__mul__(self._convert_to_int(other))
def __truediv__(self, other):
return int(self).__truediv__(self._convert_to_int(other))
def __floordiv__(self, other):
return int(self).__floordiv__(self._convert_to_int(other))
def __mod__(self, other):
return int(self).__mod__(self._convert_to_int(other))
def __divmod__(self, other):
return int(self).__divmod__(self._convert_to_int(other))
def __pow__(self, other, *args):
return int(self).__pow__(self._convert_to_int(other), *args)
def __lshift__(self, other):
return int(self).__lshift__(self._convert_to_int(other))
def __rshift__(self, other):
return int(self).__rshift__(self._convert_to_int(other))
def __and__(self, other):
return type(self)(int(self).__and__(self._convert_to_int(other)))
......@@ -144,36 +127,6 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
def __or__(self, other):
return type(self)(int(self).__or__(self._convert_to_int(other)))
def __radd__(self, other):
return int(self).__radd__(self._convert_to_int(other))
def __rsub__(self, other):
return int(self).__rsub__(self._convert_to_int(other))
def __rmul__(self, other):
return int(self).__rmul__(self._convert_to_int(other))
def __rtruediv__(self, other):
return int(self).__rtruediv__(self._convert_to_int(other))
def __rfloordiv__(self, other):
return int(self).__rfloordiv__(self._convert_to_int(other))
def __rmod__(self, other):
return int(self).__rmod__(self._convert_to_int(other))
def __rdivmod__(self, other):
return int(self).__rdivmod__(self._convert_to_int(other))
def __rpow__(self, other, *args):
return int(self).__rpow__(self._convert_to_int(other), *args)
def __rlshift__(self, other):
return int(self).__rlshift__(self._convert_to_int(other))
def __rrshift__(self, other):
return int(self).__rrshift__(self._convert_to_int(other))
def __rand__(self, other):
return type(self)(int(self).__rand__(self._convert_to_int(other)))
......@@ -183,46 +136,6 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
def __ror__(self, other):
return type(self)(int(self).__ror__(self._convert_to_int(other)))
def __iadd__(self, other):
address = type(self)(int(self).__add__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __isub__(self, other):
address = type(self)(int(self).__sub__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __imul__(self, other):
address = type(self)(int(self).__mul__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __itruediv__(self, other):
address = type(self)(int(self).__truediv__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __ifloordiv__(self, other):
address = type(self)(int(self).__floordiv__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __imod__(self, other):
address = type(self)(int(self).__mod__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __ilshift__(self, other):
address = type(self)(int(self).__ilshift__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __irshift__(self, other):
address = type(self)(int(self).__irshift__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
return self
def __iand__(self, other):
address = type(self)(int(self).__and__(self._convert_to_int(other)))
self.__buffer__ = address.__buffer__
......@@ -238,30 +151,12 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
self.__buffer__ = address.__buffer__
return self
def __neg__(self):
return -int(self)
def __pos__(self):
return int(self)
def __abs__(self):
return int(self)
def __invert__(self):
return type(self)(~int(self))
def __int__(self):
return int.from_bytes(self.__buffer__, self.__byteorder__, signed=False)
def __float__(self):
return float(int(self))
def __index__(self):
return int(self).__index__()
def __round__(self, *args):
return int(self).__round__(*args)
def __iter__(self):
yield from self.__buffer__
......@@ -273,8 +168,7 @@ class IpV4(Plum, metaclass=IpV4Type, byteorder='little'):
buffer[key] = value
if len(buffer) != 4:
raise ValueError('invalid number of address octets')
self.__buffer__ = buffer
self.__buffer__ = bytes(buffer)
def __len__(self):
return 4
......@@ -3,15 +3,14 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Integer type metaclass."""
from .. import boost
from .._plumtype import PlumType
class IpV4Type(PlumType):
class IpV4AddressType(PlumType):
"""IPV4 address metaclass.
Create custom IpV4 subclass.
Create custom IpV4Address subclass.
:param str byteorder: ``'big'`` or ``'little'``
......@@ -36,6 +35,7 @@ class IpV4Type(PlumType):
# float type which is restricted to common sizes).
cls.__byteorder__ = byteorder
cls.__little_endian__ = byteorder == 'little'
cls.__nbytes__ = 4
# FUTURE - int fast unpack should work, need special fast pack
......
......@@ -3,12 +3,12 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Interpret bytes as big endian IPV4 address."""
from .._ipv4 import IpV4 as IpV4Base
from .._ipv4address import IpV4Address as IpV4AddressBase
class Ipv4(IpV4Base, byteorder='big'):
class IpV4Address(IpV4AddressBase, byteorder='big'):
"""IPV4 Address, big endian format."""
del IpV4Base
del IpV4AddressBase
......@@ -3,12 +3,12 @@
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Interpret bytes as little endian IPV4 address."""
from .._ipv4 import IpV4 as IpV4Base
from .._ipv4address import IpV4Address as IpV4AddressBase
class IpV4(IpV4Base, byteorder='little'):
class IpV4Address(IpV4AddressBase):
"""IPV4 Address, little endian format."""
del IpV4Base
del IpV4AddressBase
......@@ -8,6 +8,6 @@ import sys
# pylint: disable=wildcard-import, unused-wildcard-import
if sys.byteorder == 'little':
from .little import *
from plum.ipv4.little import *
else:
from .big import *
from plum.ipv4.big import *
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Copyright 2020 Daniel Mark Gass, see __about__.py for license information.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
"""Test IpV4 class from little endian byte order module."""
"""Test IpV4Address class from big endian byte order module."""
from baseline import Baseline
from plum.ipv4.little import IpV4
from plum.ipv4.big import IpV4Address
from plum.test import BasicConformance
class TestIpV4(BasicConformance):
class TestIpV4Address(BasicConformance):
"""Test little endian format."""
bindata = b'\x01\x02\x03\x04'
plumtype = IpV4
plumtype = IpV4Address
cls_nbytes = 4
dump = Baseline("""
+--------+---------+-------------+------+
| Offset | Value | Bytes | Type |
+--------+---------+-------------+------+
| 0 | 1.2.3.4 | 01 02 03 04 | IpV4 |
+--------+---------+-------------+------+
+--------+---------+-------------+-------------+
| Offset | Value | Bytes | Type |
+--------+---------+-------------+-------------+
| 0 | 1.2.3.4 | 01 02 03 04 | IpV4Address |
+--------+---------+-------------+-------------+
""")
value = 0x4030201
value = 0x1020304
unpack_excess = Baseline("""
+--------+----------------+-------------+------+