Skip to content

Commit 7065ff0

Browse files
committed
ENH: provide dotted (attribute) access in stores (e.g. store.df == store['df'])
1 parent eb2c048 commit 7065ff0

File tree

6 files changed

+125
-61
lines changed

6 files changed

+125
-61
lines changed

RELEASE.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ Where to get it
2222
* Binary installers on PyPI: http://pypi.python.org/pypi/pandas
2323
* Documentation: http://pandas.pydata.org
2424

25-
- Fix weird PyTables error when using too many selectors in a where
25+
``HDFStore``
26+
27+
- Fix weird PyTables error when using too many selectors in a where
28+
- Provide dotted attribute access to ``get`` from stores (e.g. store.df == store['df'])
29+
- Internally, change all variables to be private-like (now have leading underscore)
2630

2731
pandas 0.10.1
2832
=============

doc/source/io.rst

+3
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,9 @@ In a current or later Python session, you can retrieve stored objects:
10211021
# store.get('df') is an equivalent method
10221022
store['df']
10231023
1024+
# dotted (attribute) access provides get as well
1025+
store.df
1026+
10241027
Deletion of the object specified by the key
10251028

10261029
.. ipython:: python

doc/source/v0.10.2.txt

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
.. _whatsnew_0102:
2+
3+
v0.10.2 (February ??, 2013)
4+
---------------------------
5+
6+
This is a minor release from 0.10.1 and includes many new features and
7+
enhancements along with a large number of bug fixes. There are also a number of
8+
important API changes that long-time pandas users should pay close attention
9+
to.
10+
11+
**Enhancements**
12+
13+
- In ``HDFStore``, provide dotted attribute access to ``get`` from stores (e.g. store.df == store['df'])
14+
15+
See the `full release notes
16+
<https://github.com/pydata/pandas/blob/master/RELEASE.rst>`__ or issue tracker
17+
on GitHub for a complete list.
18+

doc/source/whatsnew.rst

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ What's New
1616

1717
These are new features and improvements of note in each release.
1818

19+
.. include:: v0.10.2.txt
20+
1921
.. include:: v0.10.1.txt
2022

2123
.. include:: v0.10.0.txt

pandas/io/pytables.py

+65-56
Original file line numberDiff line numberDiff line change
@@ -197,19 +197,19 @@ def __init__(self, path, mode='a', complevel=None, complib=None,
197197
except ImportError: # pragma: no cover
198198
raise Exception('HDFStore requires PyTables')
199199

200-
self.path = path
201-
self.mode = mode
202-
self.handle = None
203-
self.complevel = complevel
204-
self.complib = complib
205-
self.fletcher32 = fletcher32
206-
self.filters = None
200+
self._path = path
201+
self._mode = mode
202+
self._handle = None
203+
self._complevel = complevel
204+
self._complib = complib
205+
self._fletcher32 = fletcher32
206+
self._filters = None
207207
self.open(mode=mode, warn=False)
208208

209209
@property
210210
def root(self):
211211
""" return the root node """
212-
return self.handle.root
212+
return self._handle.root
213213

214214
def __getitem__(self, key):
215215
return self.get(key)
@@ -220,10 +220,19 @@ def __setitem__(self, key, value):
220220
def __delitem__(self, key):
221221
return self.remove(key)
222222

223+
def __getattr__(self, name):
224+
""" allow attribute access to get stores """
225+
try:
226+
return self.get(name)
227+
except:
228+
pass
229+
raise AttributeError("'%s' object has no attribute '%s'" %
230+
(type(self).__name__, name))
231+
223232
def __contains__(self, key):
224233
""" check for existance of this key
225234
can match the exact pathname or the pathnm w/o the leading '/'
226-
"""
235+
"""
227236
node = self.get_node(key)
228237
if node is not None:
229238
name = node._v_pathname
@@ -234,7 +243,7 @@ def __len__(self):
234243
return len(self.groups())
235244

236245
def __repr__(self):
237-
output = '%s\nFile path: %s\n' % (type(self), self.path)
246+
output = '%s\nFile path: %s\n' % (type(self), self._path)
238247

239248
if len(self.keys()):
240249
keys = []
@@ -277,7 +286,7 @@ def open(self, mode='a', warn=True):
277286
mode : {'a', 'w', 'r', 'r+'}, default 'a'
278287
See HDFStore docstring or tables.openFile for info about modes
279288
"""
280-
self.mode = mode
289+
self._mode = mode
281290
if warn and mode == 'w': # pragma: no cover
282291
while True:
283292
response = raw_input("Re-opening as mode='w' will delete the "
@@ -286,36 +295,36 @@ def open(self, mode='a', warn=True):
286295
break
287296
elif response == 'n':
288297
return
289-
if self.handle is not None and self.handle.isopen:
290-
self.handle.close()
298+
if self._handle is not None and self._handle.isopen:
299+
self._handle.close()
291300

292-
if self.complib is not None:
293-
if self.complevel is None:
294-
self.complevel = 9
295-
self.filters = _tables().Filters(self.complevel,
296-
self.complib,
297-
fletcher32=self.fletcher32)
301+
if self._complib is not None:
302+
if self._complevel is None:
303+
self._complevel = 9
304+
self._filters = _tables().Filters(self._complevel,
305+
self._complib,
306+
fletcher32=self._fletcher32)
298307

299308
try:
300-
self.handle = h5_open(self.path, self.mode)
309+
self._handle = h5_open(self._path, self._mode)
301310
except IOError, e: # pragma: no cover
302311
if 'can not be written' in str(e):
303-
print 'Opening %s in read-only mode' % self.path
304-
self.handle = h5_open(self.path, 'r')
312+
print 'Opening %s in read-only mode' % self._path
313+
self._handle = h5_open(self._path, 'r')
305314
else:
306315
raise
307316

308317
def close(self):
309318
"""
310319
Close the PyTables file handle
311320
"""
312-
self.handle.close()
321+
self._handle.close()
313322

314323
def flush(self):
315324
"""
316325
Force all buffered modifications to be written to disk
317326
"""
318-
self.handle.flush()
327+
self._handle.flush()
319328

320329
def get(self, key):
321330
"""
@@ -617,14 +626,14 @@ def create_table_index(self, key, **kwargs):
617626
def groups(self):
618627
""" return a list of all the top-level nodes (that are not themselves a pandas storage object) """
619628
_tables()
620-
return [ g for g in self.handle.walkNodes() if getattr(g._v_attrs,'pandas_type',None) or getattr(g,'table',None) or (isinstance(g,_table_mod.table.Table) and g._v_name != 'table') ]
629+
return [ g for g in self._handle.walkNodes() if getattr(g._v_attrs,'pandas_type',None) or getattr(g,'table',None) or (isinstance(g,_table_mod.table.Table) and g._v_name != 'table') ]
621630

622631
def get_node(self, key):
623632
""" return the node with the key or None if it does not exist """
624633
try:
625634
if not key.startswith('/'):
626635
key = '/' + key
627-
return self.handle.getNode(self.root, key)
636+
return self._handle.getNode(self.root, key)
628637
except:
629638
return None
630639

@@ -751,7 +760,7 @@ def _write_to_group(self, key, value, index=True, table=False, append=False, com
751760

752761
# remove the node if we are not appending
753762
if group is not None and not append:
754-
self.handle.removeNode(group, recursive=True)
763+
self._handle.removeNode(group, recursive=True)
755764
group = None
756765

757766
if group is None:
@@ -768,7 +777,7 @@ def _write_to_group(self, key, value, index=True, table=False, append=False, com
768777
new_path += p
769778
group = self.get_node(new_path)
770779
if group is None:
771-
group = self.handle.createGroup(path, p)
780+
group = self._handle.createGroup(path, p)
772781
path = new_path
773782

774783
s = self._create_storer(group, value, table=table, append=append, **kwargs)
@@ -1304,28 +1313,28 @@ def pathname(self):
13041313
return self.group._v_pathname
13051314

13061315
@property
1307-
def handle(self):
1308-
return self.parent.handle
1316+
def _handle(self):
1317+
return self.parent._handle
13091318

13101319
@property
13111320
def _quiet(self):
13121321
return self.parent._quiet
13131322

13141323
@property
1315-
def filters(self):
1316-
return self.parent.filters
1324+
def _filters(self):
1325+
return self.parent._filters
13171326

13181327
@property
1319-
def complevel(self):
1320-
return self.parent.complevel
1328+
def _complevel(self):
1329+
return self.parent._complevel
13211330

13221331
@property
1323-
def fletcher32(self):
1324-
return self.parent.fletcher32
1332+
def _fletcher32(self):
1333+
return self.parent._fletcher32
13251334

13261335
@property
1327-
def complib(self):
1328-
return self.parent.complib
1336+
def _complib(self):
1337+
return self.parent._complib
13291338

13301339
@property
13311340
def attrs(self):
@@ -1380,7 +1389,7 @@ def write(self, **kwargs):
13801389
def delete(self, where = None, **kwargs):
13811390
""" support fully deleting the node in its entirety (only) - where specification must be None """
13821391
if where is None:
1383-
self.handle.removeNode(self.group, recursive=True)
1392+
self._handle.removeNode(self.group, recursive=True)
13841393
return None
13851394

13861395
raise NotImplementedError("cannot delete on an abstract storer")
@@ -1583,7 +1592,7 @@ def read_index_node(self, node):
15831592

15841593
def write_array(self, key, value):
15851594
if key in self.group:
1586-
self.handle.removeNode(self.group, key)
1595+
self._handle.removeNode(self.group, key)
15871596

15881597
# Transform needed to interface with pytables row/col notation
15891598
empty_array = any(x == 0 for x in value.shape)
@@ -1593,7 +1602,7 @@ def write_array(self, key, value):
15931602
value = value.T
15941603
transposed = True
15951604

1596-
if self.filters is not None:
1605+
if self._filters is not None:
15971606
atom = None
15981607
try:
15991608
# get the atom for this datatype
@@ -1603,9 +1612,9 @@ def write_array(self, key, value):
16031612

16041613
if atom is not None:
16051614
# create an empty chunked array and fill it from value
1606-
ca = self.handle.createCArray(self.group, key, atom,
1615+
ca = self._handle.createCArray(self.group, key, atom,
16071616
value.shape,
1608-
filters=self.filters)
1617+
filters=self._filters)
16091618
ca[:] = value
16101619
getattr(self.group, key)._v_attrs.transposed = transposed
16111620
return
@@ -1622,21 +1631,21 @@ def write_array(self, key, value):
16221631
ws = performance_doc % (inferred_type,key)
16231632
warnings.warn(ws, PerformanceWarning)
16241633

1625-
vlarr = self.handle.createVLArray(self.group, key,
1634+
vlarr = self._handle.createVLArray(self.group, key,
16261635
_tables().ObjectAtom())
16271636
vlarr.append(value)
16281637
elif value.dtype.type == np.datetime64:
1629-
self.handle.createArray(self.group, key, value.view('i8'))
1638+
self._handle.createArray(self.group, key, value.view('i8'))
16301639
getattr(self.group, key)._v_attrs.value_type = 'datetime64'
16311640
else:
16321641
if empty_array:
16331642
# ugly hack for length 0 axes
16341643
arr = np.empty((1,) * value.ndim)
1635-
self.handle.createArray(self.group, key, arr)
1644+
self._handle.createArray(self.group, key, arr)
16361645
getattr(self.group, key)._v_attrs.value_type = str(value.dtype)
16371646
getattr(self.group, key)._v_attrs.shape = value.shape
16381647
else:
1639-
self.handle.createArray(self.group, key, value)
1648+
self._handle.createArray(self.group, key, value)
16401649

16411650
getattr(self.group, key)._v_attrs.transposed = transposed
16421651

@@ -1729,7 +1738,7 @@ def write(self, obj, **kwargs):
17291738
for name, ss in obj.iteritems():
17301739
key = 'sparse_series_%s' % name
17311740
if key not in self.group._v_children:
1732-
node = self.handle.createGroup(self.group, key)
1741+
node = self._handle.createGroup(self.group, key)
17331742
else:
17341743
node = getattr(self.group, key)
17351744
s = SparseSeriesStorer(self.parent, node)
@@ -1763,7 +1772,7 @@ def write(self, obj, **kwargs):
17631772
for name, sdf in obj.iteritems():
17641773
key = 'sparse_frame_%s' % name
17651774
if key not in self.group._v_children:
1766-
node = self.handle.createGroup(self.group, key)
1775+
node = self._handle.createGroup(self.group, key)
17671776
else:
17681777
node = getattr(self.group, key)
17691778
s = SparseFrameStorer(self.parent, node)
@@ -2293,13 +2302,13 @@ def create_description(self, complib=None, complevel=None, fletcher32=False, exp
22932302

22942303
if complib:
22952304
if complevel is None:
2296-
complevel = self.complevel or 9
2305+
complevel = self._complevel or 9
22972306
filters = _tables().Filters(complevel=complevel,
22982307
complib=complib,
2299-
fletcher32=fletcher32 or self.fletcher32)
2308+
fletcher32=fletcher32 or self._fletcher32)
23002309
d['filters'] = filters
2301-
elif self.filters is not None:
2302-
d['filters'] = self.filters
2310+
elif self._filters is not None:
2311+
d['filters'] = self._filters
23032312

23042313
return d
23052314

@@ -2484,7 +2493,7 @@ def write(self, obj, axes=None, append=False, complib=None,
24842493
expectedrows=None, **kwargs):
24852494

24862495
if not append and self.is_exists:
2487-
self.handle.removeNode(self.group, 'table')
2496+
self._handle.removeNode(self.group, 'table')
24882497

24892498
# create the axes
24902499
self.create_axes(axes=axes, obj=obj, validate=append,
@@ -2502,7 +2511,7 @@ def write(self, obj, axes=None, append=False, complib=None,
25022511
self.set_attrs()
25032512

25042513
# create the table
2505-
table = self.handle.createTable(self.group, **options)
2514+
table = self._handle.createTable(self.group, **options)
25062515

25072516
else:
25082517
table = self.table
@@ -2579,7 +2588,7 @@ def delete(self, where=None, **kwargs):
25792588
# delete all rows (and return the nrows)
25802589
if where is None or not len(where):
25812590
nrows = self.nrows
2582-
self.handle.removeNode(self.group, recursive=True)
2591+
self._handle.removeNode(self.group, recursive=True)
25832592
return nrows
25842593

25852594
# infer the data kind

0 commit comments

Comments
 (0)