Skip to content

Commit 490fe65

Browse files
Added buffer of bytes round-tripping (#8)
* Added buffer of bytes round-tripping * Skip upload for PRs [ci skip] * Fix condition [ci skip]
1 parent 6b5d262 commit 490fe65

File tree

4 files changed

+81
-8
lines changed

4 files changed

+81
-8
lines changed

.appveyor.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,4 @@ test_script:
8484
- cmd: conda build conda-recipes/cyberpandas --python=${PYTHON}
8585

8686
on_success:
87-
- cmd: anaconda -t %UPLOAD_KEY% upload -u intake --force %CONDA_ROOT%\\conda-bld\\*\\*.tar.bz2
88-
on:
89-
branch: master
87+
- cmd: if "%APPVEYOR_PULL_REQUEST_NUMBER%"=="" anaconda -t %UPLOAD_KEY% upload -u intake --force %CONDA_ROOT%\\conda-bld\\*\\*.tar.bz2

cyberpandas/ip_array.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class IPType(ExtensionDtype):
3535
name = 'ip'
3636
type = IPv4v6Base
3737
kind = 'O'
38-
mybase = np.dtype([('hi', '>u8'), ('lo', '>u8')])
38+
_record_type = np.dtype([('hi', '>u8'), ('lo', '>u8')])
3939
na_value = ipaddress.IPv4Address(0)
4040

4141
@classmethod
@@ -142,6 +142,17 @@ def to_pyipaddress(self):
142142
def to_pyints(self):
143143
return [combine(*map(int, x)) for x in self.data]
144144

145+
def to_bytes(self):
146+
"""Serialize the IPArray as a Python bytestring.
147+
148+
Examples
149+
--------
150+
>>> arr = IPArray([10, 20])
151+
>>> arr.to_bytes()
152+
b'\x00\x00\...x00\x02'
153+
"""
154+
return self.data.tobytes()
155+
145156
def __repr__(self):
146157
formatted = self._format_values()
147158
return "IPArray({!r})".format(formatted)
@@ -173,6 +184,59 @@ def from_pyints(cls, values):
173184
# type: T.Sequence[int]) -> 'IPArray'
174185
return cls(_to_ipaddress_pyint(values))
175186

187+
@classmethod
188+
def from_bytes(cls, bytestring):
189+
"""Create an IPArray from a bytestring.
190+
191+
Parameters
192+
----------
193+
bytestring : bytes
194+
Note that bytestring is a Python 3-style string of bytes,
195+
not a sequences of bytes where each element represents an
196+
IPAddress.
197+
198+
Returns
199+
-------
200+
IPArray
201+
202+
Examples
203+
--------
204+
>>> arr = IPArray([10, 20])
205+
>>> buf = arr.to_bytes()
206+
>>> buf
207+
b'\x00\x00\...x00\x02'
208+
>>> IPArray.from_bytes(buf)
209+
IPArray(['0.0.0.10', '0.0.0.20'])
210+
211+
See Also
212+
--------
213+
to_bytes
214+
from_pyints
215+
"""
216+
data = np.frombuffer(bytestring, dtype=IPType._record_type)
217+
return cls._from_ndarray(data)
218+
219+
@classmethod
220+
def _from_ndarray(cls, data, copy=False):
221+
"""Zero-copy construction of an IPArray from an ndarray.
222+
223+
Parameters
224+
----------
225+
data : ndarray
226+
This should have IPType._record_type dtype
227+
copy : bool, default False
228+
Whether to copy the data.
229+
230+
Returns
231+
-------
232+
ExtensionArray
233+
"""
234+
if copy:
235+
data = data.copy()
236+
new = IPArray([])
237+
new.data = data
238+
return new
239+
176240
def __eq__(self, other):
177241
# TDOO: scalar ipaddress
178242
if not isinstance(other, IPArray):

cyberpandas/parser.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ def _to_ip_array(values):
4747

4848
if isinstance(values, IPArray):
4949
return values.data
50-
if not (isinstance(values, np.ndarray) and values.dtype == IPType.mybase):
50+
if not (isinstance(values, np.ndarray) and
51+
values.dtype == IPType._record_type):
5152
values = _to_int_pairs(values)
52-
return np.atleast_1d(np.asarray(values, dtype=IPType.mybase))
53+
return np.atleast_1d(np.asarray(values, dtype=IPType._record_type))
5354

5455

5556
def _to_int_pairs(values):
@@ -80,7 +81,7 @@ def _to_ipaddress_pyint(values):
8081
from .ip_array import IPType
8182

8283
values2 = [unpack(pack(x)) for x in values]
83-
return np.atleast_1d(np.asarray(values2, dtype=IPType.mybase))
84+
return np.atleast_1d(np.asarray(values2, dtype=IPType._record_type))
8485

8586

8687
def _as_ip_object(val):

cyberpandas/test_ip.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pandas as pd
1212
import cyberpandas as ip
1313
import pandas.util.testing as tm
14+
from cyberpandas.common import _U8_MAX
1415

1516

1617
def test_make_container():
@@ -19,7 +20,7 @@ def test_make_container():
1920
values.data,
2021
np.array([(0, 1),
2122
(0, 2),
22-
(0, 3)], dtype=values.dtype.mybase)
23+
(0, 3)], dtype=values.dtype._record_type)
2324
)
2425

2526

@@ -268,3 +269,12 @@ def test_setitem_array():
268269
ser[[1, 2]] = [10, 20]
269270
expected = ip.IPArray([0, 10, 20])
270271
assert ser.equals(expected)
272+
273+
274+
def test_bytes_roundtrip():
275+
arr = ip.IPArray([1, 2, 3, _U8_MAX + 10])
276+
bytestring = arr.to_bytes()
277+
assert isinstance(bytestring, bytes)
278+
279+
result = ip.IPArray.from_bytes(bytestring)
280+
assert result.equals(arr)

0 commit comments

Comments
 (0)