dbfread-2.0.7/0000775000175000017500000000000013016017614013326 5ustar olembolemb00000000000000dbfread-2.0.7/dbfread/0000775000175000017500000000000013016017614014715 5ustar olembolemb00000000000000dbfread-2.0.7/dbfread/deprecated_dbf.py0000664000175000017500000000331713015624542020211 0ustar olembolemb00000000000000from __future__ import print_function import sys import warnings from .dbf import DBF class DeprecatedDBF(DBF, list): """This is the old version of the table which is a subclass of list. It is included for backwards compatability with 1.0 and older.""" @property def loaded(self): # Since records are loaded into the table object # we have to check the deleted attribute here. return isinstance(self._deleted, list) def load(self): if not self.loaded: self[:] = self._iter_records(b' ') self._deleted = list(self._iter_records(b'*')) def unload(self): # self.loaded is not checked here because this # is called by __init__() where self.loaded=False. # Also, unloading twice has no consequences. del self[:] self._deleted = None def __iter__(self): if self.loaded: return list.__iter__(self) else: return self._iter_records() def __len__(self): if self.loaded: return list.__len__(self) else: return self._count_records() def __repr__(self): if self.loaded: return list.__repr__(self) else: return ''.format(self.filename) def read(filename, load=True, **kwargs): warnings.warn("dbfread.read() has been replaced by DBF(load=True)" " and will be removed in 2.2.") return DeprecatedDBF(filename, load=True, **kwargs) def open(filename, load=True, **kwargs): warnings.warn("dbfread.open() has been replaced by DBF()" " and will be removed in 2.2.") return DeprecatedDBF(filename, load=True, **kwargs) dbfread-2.0.7/dbfread/struct_parser.py0000664000175000017500000000226612627126452020205 0ustar olembolemb00000000000000""" Parser that converts (C style) binary structs named tuples. The struct can be read from a file or a byte string. """ import struct import collections def _make_struct_class(name, names): class Struct(object): _names = names def __init__(self, **kwargs): vars(self).update(kwargs) def __repr__(self): fields = ', '.join('{}={!r}'.format(name, getattr(self, name)) for name in self._names) return '{}({})'.format(self.__class__.__name__, fields) Struct.__name__ = name return Struct class StructParser: def __init__(self, name, format, names): self.format = format self.names = names self.struct = struct.Struct(format) self.Class = _make_struct_class(name, names) self.size = self.struct.size def unpack(self, data): """Unpack struct from binary string and return a named tuple.""" items = zip(self.names, self.struct.unpack(data)) return self.Class(**dict(items)) def read(self, file): """Read struct from a file-like object (implenting read()).""" return self.unpack(file.read(self.struct.size)) dbfread-2.0.7/dbfread/test_read_and_length.py0000664000175000017500000000225412670315156021436 0ustar olembolemb00000000000000""" Tests reading from database. """ from pytest import fixture import datetime from .dbf import DBF @fixture def table(): return DBF('testcases/memotest.dbf') @fixture def loaded_table(): return DBF('testcases/memotest.dbf', load=True) # This relies on people.dbf having this exact content. records = [{u'NAME': u'Alice', u'BIRTHDATE': datetime.date(1987, 3, 1), u'MEMO': u'Alice memo'}, {u'NAME': u'Bob', u'BIRTHDATE': datetime.date(1980, 11, 12), u'MEMO': u'Bob memo'}] deleted_records = [{u'NAME': u'Deleted Guy', u'BIRTHDATE': datetime.date(1979, 12, 22), u'MEMO': u'Deleted Guy memo'}] def test_len(): assert len(table()) == 2 assert len(table().deleted) == 1 assert len(loaded_table()) == 2 assert len(loaded_table().deleted) == 1 def test_list(): assert list(table()) == records assert list(table().deleted) == deleted_records assert list(loaded_table()) == records assert list(loaded_table().deleted) == deleted_records # This should not return old style table which was a subclass of list. assert not isinstance(table(), list) dbfread-2.0.7/dbfread/codepages.py0000664000175000017500000000577512435673607017255 0ustar olembolemb00000000000000# Table from dbf.py by Ethan Furman codepages = { 0x00: ('ascii', "plain ol' ascii"), 0x01: ('cp437', 'U.S. MS-DOS'), 0x02: ('cp850', 'International MS-DOS'), 0x03: ('cp1252', 'Windows ANSI'), 0x04: ('mac_roman', 'Standard Macintosh'), 0x08: ('cp865', 'Danish OEM'), 0x09: ('cp437', 'Dutch OEM'), 0x0A: ('cp850', 'Dutch OEM (secondary)'), 0x0B: ('cp437', 'Finnish OEM'), 0x0D: ('cp437', 'French OEM'), 0x0E: ('cp850', 'French OEM (secondary)'), 0x0F: ('cp437', 'German OEM'), 0x10: ('cp850', 'German OEM (secondary)'), 0x11: ('cp437', 'Italian OEM'), 0x12: ('cp850', 'Italian OEM (secondary)'), 0x13: ('cp932', 'Japanese Shift-JIS'), 0x14: ('cp850', 'Spanish OEM (secondary)'), 0x15: ('cp437', 'Swedish OEM'), 0x16: ('cp850', 'Swedish OEM (secondary)'), 0x17: ('cp865', 'Norwegian OEM'), 0x18: ('cp437', 'Spanish OEM'), 0x19: ('cp437', 'English OEM (Britain)'), 0x1A: ('cp850', 'English OEM (Britain) (secondary)'), 0x1B: ('cp437', 'English OEM (U.S.)'), 0x1C: ('cp863', 'French OEM (Canada)'), 0x1D: ('cp850', 'French OEM (secondary)'), 0x1F: ('cp852', 'Czech OEM'), 0x22: ('cp852', 'Hungarian OEM'), 0x23: ('cp852', 'Polish OEM'), 0x24: ('cp860', 'Portuguese OEM'), 0x25: ('cp850', 'Portuguese OEM (secondary)'), 0x26: ('cp866', 'Russian OEM'), 0x37: ('cp850', 'English OEM (U.S.) (secondary)'), 0x40: ('cp852', 'Romanian OEM'), 0x4D: ('cp936', 'Chinese GBK (PRC)'), 0x4E: ('cp949', 'Korean (ANSI/OEM)'), 0x4F: ('cp950', 'Chinese Big 5 (Taiwan)'), 0x50: ('cp874', 'Thai (ANSI/OEM)'), 0x57: ('cp1252', 'ANSI'), 0x58: ('cp1252', 'Western European ANSI'), 0x59: ('cp1252', 'Spanish ANSI'), 0x64: ('cp852', 'Eastern European MS-DOS'), 0x65: ('cp866', 'Russian MS-DOS'), 0x66: ('cp865', 'Nordic MS-DOS'), 0x67: ('cp861', 'Icelandic MS-DOS'), # 0x68: (None, 'Kamenicky (Czech) MS-DOS'), # 0x69: (None, 'Mazovia (Polish) MS-DOS'), 0x6a: ('cp737', 'Greek MS-DOS (437G)'), 0x6b: ('cp857', 'Turkish MS-DOS'), 0x78: ('cp950', 'Traditional Chinese ' '(Hong Kong SAR, Taiwan) Windows'), 0x79: ('cp949', 'Korean Windows'), 0x7a: ('cp936', 'Chinese Simplified (PRC, Singapore) Windows'), 0x7b: ('cp932', 'Japanese Windows'), 0x7c: ('cp874', 'Thai Windows'), 0x7d: ('cp1255', 'Hebrew Windows'), 0x7e: ('cp1256', 'Arabic Windows'), 0xc8: ('cp1250', 'Eastern European Windows'), 0xc9: ('cp1251', 'Russian Windows'), 0xca: ('cp1254', 'Turkish Windows'), 0xcb: ('cp1253', 'Greek Windows'), 0x96: ('mac_cyrillic', 'Russian Macintosh'), 0x97: ('mac_latin2', 'Macintosh EE'), 0x98: ('mac_greek', 'Greek Macintosh'), } def guess_encoding(language_driver): if language_driver in codepages: return codepages[language_driver][0] else: raise LookupError('Unable to guess encoding ' 'for languager driver byte ' '0x{:x}'.format(language_driver)) dbfread-2.0.7/dbfread/test_field_parser.py0000664000175000017500000001055613015624542020777 0ustar olembolemb00000000000000import datetime from decimal import Decimal from pytest import raises from .field_parser import FieldParser class MockHeader(object): dbversion = 0x02 class MockDBF(object): def __init__(self): self.header = MockHeader() self.encoding = 'ascii' self.char_decode_errors = 'strict' class MockField(object): def __init__(self, type='', **kwargs): self.type = type self.__dict__.update(kwargs) class MockMemoFile(dict): def __getitem__(self, index): if index == 0: return None else: return dict.__getitem__(self, index) def make_field_parser(field_type, dbversion=0x02, memofile=None): dbf = MockDBF() dbf.header.dbversion = dbversion parser = FieldParser(dbf, memofile) field = MockField(field_type) def parse(data): return parser.parse(field, data) return parse def test_0(): parse = make_field_parser('0') assert parse(b'\0') == b'\x00' assert parse(b'\xaa\xff') == b'\xaa\xff' def test_C(): parse = make_field_parser('C') assert type(parse(b'test')) == type(u'') def test_D(): parse = make_field_parser('D') assert parse(b'00000000') is None assert parse(b' ') is None epoch = datetime.date(1970, 1, 1) assert parse(b'19700101') == epoch with raises(ValueError): parse(b'NotIntgr') def test_F(): parse = make_field_parser('F') assert parse(b'') is None assert parse(b' ') is None assert parse(b'0') == 0 assert parse(b'1') == 1 assert parse(b'-1') == -1 assert parse(b'3.14') == 3.14 # In some files * is used for padding. assert parse(b'0.01**') == 0.01 assert parse(b'******') is None with raises(ValueError): parse(b'jsdf') # This also tests parse2B() (+) def test_I(): parse = make_field_parser('I') # Little endian unsigned integer. assert parse(b'\x00\x00\x00\x00') == 0 assert parse(b'\x01\x00\x00\x00') == 1 assert parse(b'\xff\xff\xff\xff') == -1 def test_L(): parse = make_field_parser('L') for char in b'TtYy': assert parse(char) is True for char in b'FfNn': assert parse(char) is False for char in b'? ': assert parse(char) is None # Some invalid values. for char in b'!0': with raises(ValueError): parse(char) # This also tests B, G and P. def test_M(): parse = make_field_parser('M', memofile=MockMemoFile({1: b'test'})) assert parse(b'\x01\x00\x00\x00') == u'test' assert parse(b'1') == u'test' assert parse(b'') is None with raises(ValueError): parse(b'NotInteger') def test_B(): # In VisualFox the B field is a double precision floating point number. parse = make_field_parser('B', dbversion=0x30) assert isinstance(parse(b'01abcdef'), float) assert parse(b'\0' * 8) == 0.0 # Data must be exactly 8 bytes. with raises(Exception): parse(b'') # In other db versions it is a memo index. parse = make_field_parser('B', dbversion=0x02, memofile=MockMemoFile({1: b'test'})) parse(b'1') == b'test' parse(b'') is None def test_N(): parse = make_field_parser('N') assert parse(b'') is None assert parse(b' ') is None assert parse(b'1') == 1 assert parse(b'-99') == -99 assert parse(b'3.14') == 3.14 # In some files * is used for padding. assert parse(b'0.01**') == 0.01 assert parse(b'******') is None with raises(ValueError): parse(b'okasd') def test_O(): """Test double field.""" parse = make_field_parser('O') assert parse(b'\x00' * 8) == 0.0 assert parse(b'\x00\x00\x00\x00\x00\x00\xf0?') == 1.0 assert parse(b'\x00\x00\x00\x00\x00\x00Y\xc0') == -100 # This also tests parse40() (@) def test_T(): parse = make_field_parser('T') assert parse(b'') is None assert parse(b' ') is None # Todo: add more tests. def test_Y(): parse = make_field_parser('Y') assert parse(b'\1\0\0\0\0\0\0\0') == Decimal('0.0001') assert parse(b'\xff\xff\xff\xff\xff\xff\xff\xff') == Decimal('-0.0001') def test_hex_field(): class PlusFieldParser(FieldParser): encoding = 'latin1' def parse3F(self, field, data): """Parser for '?' field.""" return None parser = PlusFieldParser(MockDBF()) field = MockField('?') parser.parse(field, b'test') dbfread-2.0.7/dbfread/dbf.py0000664000175000017500000002376413015624542016041 0ustar olembolemb00000000000000""" Class to read DBF files. """ import os import sys import datetime import collections from .ifiles import ifind from .struct_parser import StructParser from .field_parser import FieldParser from .memo import find_memofile, open_memofile, FakeMemoFile, BinaryMemo from .codepages import guess_encoding from .dbversions import get_dbversion_string from .exceptions import * DBFHeader = StructParser( 'DBFHeader', ' 255 bytes the high byte # is stored in decimal_count. if field.type in 'C': field.length |= field.decimal_count << 8 field.decimal_count = 0 # Field name is b'\0' terminated. field.name = self._decode_text(field.name.split(b'\0')[0]) if self.lowernames: field.name = field.name.lower() self.field_names.append(field.name) self.fields.append(field) def _open_memofile(self): if self.memofilename and not self.raw: return open_memofile(self.memofilename, self.header.dbversion) else: return FakeMemoFile(self.memofilename) def _check_headers(self): field_parser = self.parserclass(self) """Check headers for possible format errors.""" for field in self.fields: if field.type == 'I' and field.length != 4: message = 'Field type I must have length 4 (was {})' raise ValueError(message.format(field.length)) elif field.type == 'L' and field.length != 1: message = 'Field type L must have length 1 (was {})' raise ValueError(message.format(field.length)) elif not field_parser.field_type_supported(field.type): # Todo: return as byte string? raise ValueError('Unknown field type: {!r}'.format(field.type)) def _skip_record(self, infile): # -1 for the record separator which was already read. infile.seek(self.header.recordlen - 1, 1) def _count_records(self, record_type=b' '): count = 0 with open(self.filename, 'rb') as infile: # Skip to first record. infile.seek(self.header.headerlen, 0) while True: sep = infile.read(1) if sep == record_type: count += 1 self._skip_record(infile) elif sep in (b'\x1a', b''): # End of records. break else: self._skip_record(infile) return count def _iter_records(self, record_type=b' '): with open(self.filename, 'rb') as infile, \ self._open_memofile() as memofile: # Skip to first record. infile.seek(self.header.headerlen, 0) if not self.raw: field_parser = self.parserclass(self, memofile) parse = field_parser.parse # Shortcuts for speed. skip_record = self._skip_record read = infile.read while True: sep = read(1) if sep == record_type: if self.raw: items = [(field.name, read(field.length)) \ for field in self.fields] else: items = [(field.name, parse(field, read(field.length))) \ for field in self.fields] yield self.recfactory(items) elif sep in (b'\x1a', b''): # End of records. break else: skip_record(infile) def __iter__(self): if self.loaded: return list.__iter__(self._records) else: return self._iter_records() def __len__(self): return len(self.records) def __repr__(self): if self.loaded: status = 'loaded' else: status = 'unloaded' return '<{} DBF table {!r}>'.format(status, self.filename) def __enter__(self): return self def __exit__(self, type, value, traceback): self.unload() return False dbfread-2.0.7/dbfread/memo.py0000664000175000017500000000775213004622442016235 0ustar olembolemb00000000000000""" Reads data from FPT (memo) files. FPT files are used to varying lenght text or binary data which is too large to fit in a DBF field. VFP == Visual FoxPro DB3 == dBase III DB4 == dBase IV """ from collections import namedtuple from .ifiles import ifind from .struct_parser import StructParser VFPFileHeader = StructParser( 'FPTHeader', '>LHH504s', ['nextblock', 'reserved1', 'blocksize', 'reserved2']) VFPMemoHeader = StructParser( 'FoxProMemoHeader', '>LL', ['type', 'length']) DB4MemoHeader = StructParser( 'DBase4MemoHeader', ' '/path/to/[Tt][Ee][Ss][Tt].[]' newpat = '' for c in pat: if c.isalpha: u = c.upper() l = c.lower() if u != l: newpat = newpat + '[' + u + l + ']' else: newpat += c else: newpat += c newpat = os.path.join(dirname, newpat) return newpat def ifnmatch(name, pat): """Case insensitive version of fnmatch.fnmatch()""" return fnmatch.fnmatch(name, ipat(pat)) def iglob(pat): """Case insensitive version of glob.glob()""" return glob.glob(ipat(pat)) def ifind(pat, ext=None): """Look for a file in a case insensitive way. Returns filename it a matching file was found, or None if it was not. """ if ext: pat = os.path.splitext(pat)[0] + ext files = iglob(pat) if files: return files[0] # Return an arbitrary file else: return None __all__ = ['ipat', 'ifnmatch', 'iglob', 'ifind'] dbfread-2.0.7/dbfread/field_parser.py0000664000175000017500000002054013015624542017732 0ustar olembolemb00000000000000""" Parser for DBF fields. """ import sys import datetime import struct from decimal import Decimal from .memo import BinaryMemo PY2 = sys.version_info[0] == 2 if PY2: decode_text = unicode else: decode_text = str class InvalidValue(bytes): def __repr__(self): text = bytes.__repr__(self) if PY2: # Make sure the string starts with "b'" in # "InvalidValue(b'value here')". text = 'b' + text return 'InvalidValue({})'.format(text) class FieldParser: def __init__(self, table, memofile=None): """Create a new field parser encoding is the character encoding to use when parsing strings.""" self.table = table self.dbversion = self.table.header.dbversion self.encoding = table.encoding self.char_decode_errors = table.char_decode_errors self._lookup = self._create_lookup_table() if memofile: self.get_memo = memofile.__getitem__ else: self.get_memo = lambda x: None def decode_text(self, text): return decode_text(text, self.encoding, errors=self.char_decode_errors) def _create_lookup_table(self): """Create a lookup table for field types.""" lookup = {} for name in dir(self): if name.startswith('parse'): field_type = name[5:] if len(field_type) == 1: lookup[field_type] = getattr(self, name) elif len(field_type) == 2: # Hexadecimal ASCII code for field name. # Example: parse2B() ('+' field) field_type = chr(int(field_type, 16)) lookup[field_type] = getattr(self, name) return lookup def field_type_supported(self, field_type): """Checks if the field_type is supported by the parser field_type should be a one-character string like 'C' and 'N'. Returns a boolen which is True if the field type is supported. """ return field_type in self._lookup def parse(self, field, data): """Parse field and return value""" try: func = self._lookup[field.type] except KeyError: raise ValueError('Unknown field type: {!r}'.format(field.type)) else: return func(field, data) def parse0(self, field, data): """Parse flags field and return as byte string""" return data def parseC(self, field, data): """Parse char field and return unicode string""" return self.decode_text(data.rstrip(b'\0 ')) def parseD(self, field, data): """Parse date field and return datetime.date or None""" try: return datetime.date(int(data[:4]), int(data[4:6]), int(data[6:8])) except ValueError: if data.strip(b' 0') == b'': # A record containing only spaces and/or zeros is # a NULL value. return None else: raise ValueError('invalid date {!r}'.format(data)) def parseF(self, field, data): """Parse float field and return float or None""" # In some files * is used for padding. data = data.strip().strip(b'*') if data: return float(data) else: return None def parseI(self, field, data): """Parse integer or autoincrement field and return int.""" # Todo: is this 4 bytes on every platform? return struct.unpack('>> from dbfread import DBF >>> for record in DBF('people.dbf'): ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) Full documentation at https://dbfread.readthedocs.io/ """ __author__ = 'Ole Martin Bjorndalen' __email__ = 'ombdalen@gmail.com' __url__ = 'https://dbfread.readthedocs.io/' __license__ = 'MIT' from .dbf import DBF from .deprecated_dbf import open, read from .exceptions import * from .field_parser import FieldParser, InvalidValue from .version import version_info, version as __version__ # Prevent splat import. __all__ = [] dbfread-2.0.7/setup.py0000775000175000017500000000157613016017607015056 0ustar olembolemb00000000000000#!/usr/bin/env python import os import sys import dbfread try: from setuptools import setup except ImportError: from distutils.core import setup if sys.argv[-1] == "publish": os.system("python setup.py sdist upload") sys.exit() elif sys.argv[-1] == "test": os.system("./run_tests.py") sys.exit() elif sys.argv[-1] == "docs": os.system("sphinx-build docs/ docs/_build") sys.exit() setup( name='dbfread', version=dbfread.__version__, description='Read DBF Files with Python', long_description=open('README.rst', 'rt').read(), author=dbfread.__author__, author_email=dbfread.__email__, url=dbfread.__url__, package_data={'': ['LICENSE']}, package_dir={'dbfread': 'dbfread'}, packages = ['dbfread'], include_package_data=True, zip_safe=True, install_requires=[], license='MIT', classifiers=( ), ) dbfread-2.0.7/docs/0000775000175000017500000000000013016017614014256 5ustar olembolemb00000000000000dbfread-2.0.7/docs/api_changes.rst0000664000175000017500000000134112634101526017251 0ustar olembolemb00000000000000API Changes =========== ``dbfread.open()`` and ``dbfread.read()`` are deprecated as of version ``2.0``, and will be removed in ``2.2``. The ``DBF`` class is no longer a subclass of ``list``. This makes the API a lot cleaner and easier to understand, but old code that relied on this behaviour will be broken. Iteration and record counting works the same as before. Other list operations can be rewritten using the ``record`` attribute. For example:: table = dbfread.read('people.dbf') print(table[1]) can be rewritten as:: table = DBF('people.dbf', load=True) print(table.records[1]) ``open()`` and ``read()`` both return ``DeprecatedDBF``, which is a subclass of ``DBF`` and ``list`` and thus backward compatible. dbfread-2.0.7/docs/_static/0000775000175000017500000000000013016017614015704 5ustar olembolemb00000000000000dbfread-2.0.7/docs/_static/PLACEHOLDER0000664000175000017500000000007012435673607017364 0ustar olembolemb00000000000000This file is needed for git to include this directory. dbfread-2.0.7/docs/conf.py0000664000175000017500000001725712435673607015607 0ustar olembolemb00000000000000# -*- coding: utf-8 -*- # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) import dbfread from dbfread import __version__ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'dbfread' copyright = u'Ole Martin Bjørndalen' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'dbfreaddoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'dbfread.tex', u'dbfread Documentation', u'Ole Martin Bjørndalen', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'dbfread', u'dbfread Documentation', [u'Ole Martin Bjørndalen'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'dbfread', u'dbfread Documentation', u'Ole Martin Bjørndalen', 'dbfread', 'Read DBF files with Python', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' dbfread-2.0.7/docs/acknowledgements.rst0000664000175000017500000000021712435673607020360 0ustar olembolemb00000000000000Acknowledgements ================ The code page table is based on the one in Ethan Furman's `dbf `_ package. dbfread-2.0.7/docs/changes.rst0000664000175000017500000001444613015624542016434 0ustar olembolemb00000000000000Changes ======= Release History --------------- 2.0.7 - 2016-11-24 ^^^^^^^^^^^^^^^^^^ * Sometimes numeric (N) and float fields (F) are padded with '*'. These are now stripped. (Reported by sgiangola and Matungos, issue #10.) * added ``char_decode_errors`` option which lets you choose how to handle characters that can't be decoded. (Implemented by ZHU Enwei, pull request #16.) * added ``--char-decode-errors`` option to ``dbf2sqlite``. * added ``dbfread.version_info``. 2.0.6 - 2016-06-07 ^^^^^^^^^^^^^^^^^^ * Added support for long character (C) fields (up to 65535 bytes). (Requested by Eric Mertens and Marcelo Manzano.) * Added support for Visual FoxPro varchar fields (V). (Thanks to Roman Kharin for reporting and bobintetley for providing a solution.) * Bugfix (dbf2sqlite): some table or field names might actually collide with sql reserved words. (Fix by vthriller, pull request #15.) * Documented how to convert records to Pandas data frames. (Thanks to Roman Yurchak for suggesting this.) 2.0.5 - 2015-11-30 ^^^^^^^^^^^^^^^^^^ * Bugfix: memo field parser used str instead of bytes. (Fix submitted independently by Sebastian Setzer (via email) and by Artem Vlasov, pull request #11.) * Bugfix: some field parsers called self._get_memo() instead of self.get_memo(). (Fix by Yu Feng, pull request #9.) 2.0.4 - 2015-02-07 ^^^^^^^^^^^^^^^^^^ * DBF header and field headers are no longer read-only. For example you can now change field names by doing ``table.fields[0].name = 'price'`` or read from files where field sizes in the header don't match those in the actual records by doing ``table.fields[0].length = 500``. * fixed some examples that didn't work with Python 3. 2.0.3 - 2014-09-30 ^^^^^^^^^^^^^^^^^^ * added currency field (Y). (Patch by Stack-of-Pancakes.) 2.0.2 - 2014-09-29 ^^^^^^^^^^^^^^^^^^ * bugfix: a date with all zeroes in the DBF header resulted in 'ValueError: month must be in 1..12'. (Reported by Andrew Myers.) The ``date`` attribute is now set to ``None`` for any value that is not a valid date. 2.0.1 - 2014-09-19 ^^^^^^^^^^^^^^^^^^ * bugfix: didn't handle field names with garbage after b'\0' terminator. (Patch by Cédric Krier.) * now handles 0 (_NullFlags) fields that are more than 1 byte long. 0 fields are now returned as byte strings instead of integers. (Reported by Carlos Huga.) * the type B field is a double precision floating point numbers in Visual FoxPro. The parser crashed when it tried to interpret this as a string containing a number. (Reported by Carlos Huga.) * API changes: memo field parsers now return the memo data (typically a unicode string or bytes object) instead of returning the index. This makes it easier to implement new memo types or extend the existing ones since memo fields are no longer a special case. 2.0.0 - 2014-08-12 ^^^^^^^^^^^^^^^^^^ * ``dbfread.open()`` and ``dbfread.read()`` are now deprecated and will be removed in 1.4. Since the ``DBF`` object is no longer a subclass of list, these functions instead return backward compatible ``DeprecatedDBF`` objects. * records are now returned as ordered dictionaries. This makes it easier to iterate over fields in the same order that they appear in the file. * now reads (at least some) DBT files. * added support for 6 new field types. * added ``ignore_missing_memofile`` argument. If ``True`` and the memo file is not found all memo fields will be returned as ``None``. * DBF now raises ``DBFNotFound`` and ``MissingMemoFile``. These inherit from IOError, so old code should still work. * added ``InvalidValue``. This is currently not used by the library but can be useful for custom parsing. * ``FieldParser`` is now available in the top scope. * wrote documentation. * switched to pytest for unit tests. 1.1.1 - 2014-08-03 ^^^^^^^^^^^^^^^^^^ * example and test data files were missing from the manifest. 1.1.0 - 2014-08-03 ^^^^^^^^^^^^^^^^^^ * the ``DBF`` object is no longer a subclass of list. Records are instead available in the ``records`` attribute, but the table can be iterated over like before. This change was made to make the API cleaner and easier to understand. ``read()`` is still included for backwards compatability, and returns an ``OldStyleTable`` object with the old behaviour. * default character encoding is now ``"ascii"``. This is a saner default than the previously used ``"latin1"``, which would decode but could give the wrong characters. * the DBF object can now be used as a context manager (using the "with" statement). 1.0.6 - 2014-08-02 ^^^^^^^^^^^^^^^^^^ * critical bugfix: each record contained only the last field. (Introduced in 1.0.5, making that version unusable.) * improved performance of record reading a bit. 1.0.5 - 2014-08-01 ^^^^^^^^^^^^^^^^^^ This version is broken. * more than doubled performance of record parsing. * removed circular dependency between table and deleted record iterator. * added ``dbversion`` attribute. * added example ``dbfinfo.py``. * numeric field (N) parser now handles invalid data correctly. * added more unit tests. 1.0.4 - 2014-07-27 ^^^^^^^^^^^^^^^^^^ * bugfix: crashed when record list was not terminated with b'\x1a'. (Bug first apperad in 1.0.2 after a rewrite.) * bugfix: memo fields with no value were returned as ''. They are now returned correctly as None. * bugfix: field header terminaters were compared with strings. * added example parserclass_debugstring.py. 1.0.3 - 2014-07-26 ^^^^^^^^^^^^^^^^^^ * reinstated hastily removed parserclass option. 1.0.2 - 2014-07-26 ^^^^^^^^^^^^^^^^^^ * added example record_objects.py. * removed parserclass option to allow for internal changes. There is currently no (documented) way to add custom field types. 1.0.1 - 2014-07-26 ^^^^^^^^^^^^^^^^^^ * bugfix: deleted records were ignored when using open(). * memo file is now opened and closed by each iterator instead of staying open all the time. 1.0.0 - 2014-07-25 ^^^^^^^^^^^^^^^^^^ * records can now be streamed from the file, making it possible to read data files that are too large to fit in memory. * documentation is more readable and complete. * now installs correctly with easy_install. * added "--encoding" option to dbf2sqlite which can be used to override character encoding. 0.1.0 - 2014-04-08 ^^^^^^^^^^^^^^^^^^ Initial release. dbfread-2.0.7/docs/license.rst0000664000175000017500000000216312435673607016452 0ustar olembolemb00000000000000License ======= The MIT License (MIT) Copyright (c) Ole Martin Bjørndalen / UiT The Arctic University of Norway Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dbfread-2.0.7/docs/dbf_objects.rst0000664000175000017500000001212613015624542017261 0ustar olembolemb00000000000000DBF Objects =========== Arguments --------- filename The DBF file to open. The file name is case insensitive, which means ``DBF('PEOPLE.DBF')`` will open the file ``people.dbf``. If there is a memo file, it too will be looked for in a case insensitive manner, so ``DBF('PEOPLE.DBF')`` would find the memo file ``people.FPT``. ``DBFNotFound`` will be raised if the file is not found, and ``MissingMemoFile`` if the memo file is missing. load=False By default records will streamed directly from disk. If you pass ``load=True`` they will instead be loaded into lists and made available as the ``records`` and ``deleted`` attributes. You can load and unload records at any time with the ``load()`` and ``unload()`` methods. encoding=None Specify character encoding to use. By default dbfread will try to guess character encoding from the ``language_driver`` byte. If this fails it falls back on ASCII. char_decode_errors='strict' The error handling scheme to use for the handling of decoding errors. This is passed as the ``errors`` option to the ``bytes.decode()`` method. From the documentation of that method: "The default is 'strict' meaning that decoding errors raise a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' as well as any other name registered with codecs.register_error that can handle UnicodeDecodeErrors." lowernames=False Field names are typically uppercase. If you pass ``True`` all field names will be converted to lowercase. recfactory=collections.OrderedDict Takes a function that will be used to produce new records. The function will be called with a list of ``(name, value)`` pairs. If you pass ``recfactory=None`` you will get the original ``(name, value)`` list. ignorecase=True Windows uses a case preserving file system which means ``people.dbf`` and ``PEOPLE.DBF`` are the same file. This causes problems in for example Linux where case is significant. To get around this dbfread ignores case in file names. You can turn this off by passing ``ignorecase=False``. parserclass=FieldParser The parser to use when parsing field values. You can use this to add new field types or do custom parsing by subclassing ``dbfread.FieldParser``. (See :doc:`field_types`.) ignore_missing_memofile=False If you don't have the memo field you can pass ``ignore_missing_memofile=True``. All memo fields will then be returned as ``None``, so you at least get the rest of the data. raw=False Returns all data values as byte strings. This can be used for debugging or for doing your own decoding. Methods ------- load() Load records into memory. This loads both records and deleted records. The ``records`` and ``deleted`` attributes will now be lists of records. unload() Unload records from memory. The ``records`` and ``deleted`` attributes will now be instances of ``RecordIterator``, which streams records from disk. Attributes ---------- records If the table is loaded this is a list of records. If not, it's a ``RecordIterator`` object. In either case, iterating over it or calling ``len()`` on it will give the same results. deleted If the table is loaded this is a list of deleted records. If not, it's a ``RecordIterator`` object. In either case, iterating over it or calling ``len()`` on it will give the same results. loaded ``True`` if records are loaded into memory. dbversion The name of the program that created the database (based on the ``dbversion`` byte in the header). Example: ``"FoxBASE+/Dbase III plus, no memory"``. name Name of the table. This is the lowercased stem of the filename, for example the file ``/home/me/SHOES.dbf`` will have the name ``shoes``. date Date when the file was last updated (as ``datetime.date``) or ``None`` if the date was all zeroes or invalid. field_names A list of field names in the order they appear in the file. This can for example be used to produce the header line in a CSV file. encoding Character encoding used in the file. This is determined by the ``language_driver`` byte in the header, and can be overriden with the ``encoding`` keyword argument. ignorecase, lowernames, recfactory, parserclass, raw These are set to the values of the same keyword arguments. filename File name of the DBF file. memofilename File name of the memo file, or ``None`` if there is no memo file. header The file header. This is only intended for internal use, but is exposed for debugging purposes. Example:: DBFHeader(dbversion=3, year=114, month=8, day=2, numrecords=3, headerlen=97, recordlen=25, reserved1=0, incomplete_transaction=0, encryption_flag=0, free_record_thread=0, reserved2=0, reserved3=0, mdx_flag=0, language_driver=0, reserved4=0) fields A list of field headers from the file. Example of a field:: DBFField(name='NAME', type='C', address=1, length=16, decimal_count=0, reserved1=0, workarea_id=0, reserved2=0, reserved3=0, set_fields_flag=0, reserved4=b'\x00\x00\x00\x00\x00\x00\x00', index_field_flag=0) Only the ``name``, ``type`` and ``length`` attributes are used. dbfread-2.0.7/docs/field_types.rst0000664000175000017500000001034413015624542017324 0ustar olembolemb00000000000000Field Types =========== Supported Field Types --------------------- == ============= ======================================================== : Field type Converted to == ============= ======================================================== \+ autoincrement int @ time datetime.datetime 0 flags byte string (int before 2.0) B double float (Visual FoxPro) B binary memo byte string (other versions) C text unicode string D date datetime.date or None F float float G OLE object byte string I integer int L logical True, False or None M memo unicode string (memo), byte string (picture or object) or None N numeric int, float or None O double float (floats are doubles in Python) P picture byte string T time datetime.datetime V varchar unicode string Y currency decimal.Decimal == ============= ======================================================== Text values ('C') can be up to 65535 bytes long. DBF was originally limited to 255 bytes but some vendors have reused the ``decimal_count`` field to get another byte for field length. The 'B' field type is used to store double precision (64 bit) floats in Visual FoxPro databases and binary memos in other versions. ``dbfread`` will look at the database version to parse and return the correct data type. The '0' field type is used for '_NullFlags' in Visual FoxPro. It was mistakenly though to always be one byte long and was interpreted as an integer. From 2.0.1 on it is returned as a byte string. The 'V' field is an alternative character field used by Visual FoxPro. The binary version of this field is not yet supported. (See https://msdn.microsoft.com/en-us/library/st4a0s68%28VS.80%29.aspx for more.) Adding Custom Field Types ------------------------- You can add new field types by subclassing :py:class:`FieldParser`. For example: .. literalinclude:: ../examples/custom_field_type.py The ``FieldParser`` object has the following attributes: self.table A reference to the ``DBF`` objects. This can be used to get the headers to find ``dbversion`` and other things. self.encoding The character encoding. (A a shortcut for ``self.table.encoding`` to speed things up a bit.) self.char_decode_errors Error handling scheme to use while decoding. (A shortcut for ``self.table.char_decode_errors``.) self.dbversion The database version as an integer. (A shortcut for ``self.table.header.dbversion``.) self.get_memo(index) Returns a memo from the memo file using the index stored in the field data. This returns a byte string (``bytes``) which you can then decode. For Visual FoxPro (``.FPT``) files it will return ``TextMemo``, ``PictureMemo`` and ``ObjectMemo`` objects depending on the type of memo. These are all subclasses of ``bytes`` so the type is only used to annotate the memo type without breaking code elsewhere. The full class tree:: bytes VFPMemo TextMemo BinaryMemo PictureMemo ObjectMemo These are all found in ``dbfread.memo``. self.decode_text(text) This will decode the text using the correct encoding and the user supplied ``char_decode_errors`` option. Special Characters in Field Type Names -------------------------------------- For a field type like '+' (autoincrement) the method would be named ``parse+()``. Since this is not allowed in Python you can instead use its ASCII value in hexadecimal. For example, the '+' parser is called ``parse3F()``. You can name your method with:: >>> 'parse' + format(ord('?'), 'x').upper() 'parse3F' Just replace ``'?'`` with your field type. InvalidValue ------------ The field parser will normally raise ``ValueError`` when invalid values are encountered. If instead you want them returned as raw data you can do this: .. literalinclude:: ../examples/print_invalid_values.py ``InvalidValue`` is a subclass of ``bytes``, and allows you to tell invalid data apart from valid data that happens to be byte strings. You can test for this with:: isinstance(value, InvalidData) You can also tell from the ``repr()`` string:: >>> value InvalidData(b'not a number') dbfread-2.0.7/docs/exporting_data.rst0000664000175000017500000000275113015624542020030 0ustar olembolemb00000000000000.. highlight:: python Moving data to SQL, CSV, Pandas etc. ==================================== CSV --- This uses the standard library ``csv`` module: .. literalinclude:: ../examples/print_csv.py The output is:: NAME,BIRTHDATE Alice,1987-03-01 Bob,1980-11-12 Pandas Data Frames ------------------ .. literalinclude:: ../examples/pandas_dataframe.py This will print:: BIRTHDATE NAME 0 1987-03-01 Alice 1 1980-11-12 Bob The ``iter()`` is required. Without it Pandas will not realize that it can iterate over the table. Pandas will create a new list internally before converting the records to data frames. This means they will all be loaded into memory. There seems to be no way around this at the moment. dataset (SQL) ------------- The `dataset `_ package makes it easy to move data to a modern database. Here's how you can insert the ``people`` table into an SQLite database: .. literalinclude:: ../examples/using_dataset.py (This also creates the schema.) dbf2sqlite ---------- You can use the included example program ``dbf2sqlite`` to insert tables into an SQLite database:: dbf2sqlite -o example.sqlite table1.dbf table2.dbf This will create one table for each DBF file. You can also omit the ``-o example.sqlite`` option to have the SQL printed directly to stdout. If you get character encoding errors you can pass ``--encoding`` to override the encoding, for example:: dbf2sqlite --encoding=latin1 ... dbfread-2.0.7/docs/introduction.rst0000664000175000017500000001366713015624542017551 0ustar olembolemb00000000000000.. highlight:: python Introduction ============ This is a short introduction to the API. If you want to follow along you can find ``people.dbf`` in ``examples/files/``. Opening a DBF File ------------------ :: >>> from dbfread import DBF >>> table = DBF('people.dbf') This returns a ``DBF`` object. You can now iterate over records:: >>> for record in table: ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) and count records:: >>> len(table) 2 Deleted records are available in ``deleted``:: >>> for record in table.deleted: ... print(record) OrderedDict([('NAME', 'Deleted Guy'), ('BIRTHDATE', datetime.date(1979, 12, 22))]) >>> len(table.deleted) 1 You can also use the ``with`` statement:: with DBF('people.dbf') as table: ... The DBF object doesn't keep any files open, so this is provided merely as a convenience. Streaming or Loading Records ---------------------------- By default records are streamed directly off disk, which means only one record is in memory at a time. If have enough memory, you can load the records into a list by passing ``load=True``. This allows for random access:: >>> table = DBF('people.dbf', load=True) >>> print(table.records[1]['NAME']) Bob >>> print(table.records[0]['NAME']) Alice Deleted records are also loaded into a list in ``table.deleted``. Alternatively, you can load the records later by calling ``table.load()``. This is useful when you want to look at the header before you commit to loading anything. For example, you can make a function which returns a list of tables in a directory and load only the ones you need. If you just want a list of records and you don't care about the other table attributes you can do:: >>> records = list(DBF('people.dbf')) You can unload records again with ``table.unload()``. If the table is not loaded, the ``records`` and ``deleted`` attributes return ``RecordIterator`` objects. Loading or iterating over records will open the DBF and memo file once for each iteration. This means the ``DBF`` object doesn't hold any files open, only the ``RecordIterator`` object does. Character Encodings ------------------- All text fields and memos (except binary ones) will be returned as unicode strings. dbfread will try to detect the character encoding (code page) used in the file by looking at the ``language_driver`` byte. If this fails it reverts to ASCII. You can override this by passing ``encoding='my-encoding'``. The encoding is available in the ``encoding`` attribute. There may still be characters that won't decode. You can choose how to handle these by passing the ``char_decode_errors`` option. This is passed straight to ``bytes.decode``. See ``pydoc bytes.decode`` for more. Memo Files ---------- If there is at least one memo field in the file dbfread will look for the corresponding memo file. For ``buildings.dbf`` this would be ``buildings.fpt`` (for Visual FoxPro) or ``buildings.dbt`` (for other databases). Since the Windows file system is case preserving, the file names may end up mixed case. For example, you could have:: Buildings.dbf BUILDINGS.DBT This creates problems in Linux, where file names are case sensitive. dbfread gets around this by ignoring case in file names. You can turn this off by passing ``ignorecase=False``. If the memo file is missing you will get a ``MissingMemoFile`` exception. If you still want the rest of the data you can pass ``ignore_missing_memofile=True``. All memo field values will now be returned as ``None``, as would be the case if there was no memo. dbfread has full support for Visual FoxPro (``.FPT``) and dBase III (``.DBT``) memo files. It reads dBase IV (also ``.DBT``) memo files, but only if they use the default block size of 512 bytes. (This will be fixed if I can find more files to study.) Record Factories ---------------- If you don't want records returned as ``collections.OrderedDict`` you can use the ``recfactory`` argument to provide your own record factory. A record factory is a function that takes a list of ``(name, value)`` pairs and returns a record. You can do whatever you like with this data. Here's a function that creates a record object with fields as attributes:: class Record(object): def __init__(self, items): for (name, value) in items: setattr(self, name, value) for record in DBF('people.dbf', recfactory=Record, lowernames=True): print(record.name, record.birthdate) If you pass ``recfactory=None`` you will get the original ``(name, value)`` list. (This is a shortcut for ``recfactory=lambda items: items``.) Custom Field Types ------------------ If the included message types are not enough you can add your own by subclassing ``FieldParser``. As a silly example, here how you can read text (``C``) fields in reverse:: from dbfread import DBF, FieldParser class MyFieldParser(FieldParser): def parseC(self, field, data): # Return strings reversed. return data.rstrip(' 0').decode()[::-1] for record in DBF('files/people.dbf', parserclass=MyFieldParser): print(record['NAME']) and here's how you can return invalid values as ``InvalidValue`` instead of raising ``ValueError``:: from dbfread import DBF, FieldParser, InvalidValue class MyFieldParser(FieldParser): def parse(self, field, data): try: return FieldParser.parse(self, field, data) except ValueError: return InvalidValue(data) table = DBF('invalid_value.dbf', parserclass=MyFieldParser): for i, record in enumerate(table): for name, value in record.items(): if isinstance(value, InvalidValue): print('records[{}][{!r}] == {!r}'.format(i, name, value)) This will print:: records[0][u'BIRTHDATE'] == InvalidValue(b'NotAYear') dbfread-2.0.7/docs/index.rst0000664000175000017500000000263712725544010016130 0ustar olembolemb00000000000000.. dbfread documentation master file You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. dbfread - Read DBF Files with Python ==================================== Version |version| DBF is a file format used by databases such dBase, Visual FoxPro, and FoxBase+. This library reads DBF files and returns the data as native Python data types for further processing. It is primarily intended for batch jobs and one-off scripts. .. code-block:: python >>> from dbfread import DBF >>> for record in DBF('people.dbf'): ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) Source code ----------- Latest stable release: https://github.com/olemb/dbfread/ Latest development version: https://github.com/olemb/dbfread/tree/develop/ About This Document ------------------- This document is available at https://dbfread.readthedocs.io/ To build documentation locally:: python setup.py docs This requires Sphinx. The resulting files can be found in ``docs/_build/``. Contents ------------ .. toctree:: :maxdepth: 2 changes installing introduction exporting_data dbf_objects field_types api_changes resources license acknowledgements dbfread-2.0.7/docs/resources.rst0000664000175000017500000000154512435673607017045 0ustar olembolemb00000000000000Resources ========= DBF file format documentation ----------------------------- * `Xbase File Format Description `_ by Erik Bachmann * `Data File Header Structure for the dBASE Version 7 Table File `_ * `Wikipedia article about dBase `_ * `DBF Field Types and Specifications `_ * `DBase File Structure `_ * `dBase IV limitations `_ * `DBF Table File Structure (Microsoft Developer Network) `_ dbfread-2.0.7/docs/make.bat0000664000175000017500000001175212435673607015707 0ustar olembolemb00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\dbfread.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\dbfread.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end dbfread-2.0.7/docs/installing.rst0000664000175000017500000000036712435673607017200 0ustar olembolemb00000000000000Installing dbfread ================== Requirements ------------ Requires Python 3.2 or 2.7. ``dbfread`` is a pure Python module, so doesn't depend on any packages outside the standard library. Installing ---------- :: pip install dbfread dbfread-2.0.7/docs/Makefile0000664000175000017500000001270012435673607015734 0ustar olembolemb00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/dbfread.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/dbfread.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/dbfread" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/dbfread" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." dbfread-2.0.7/MANIFEST.in0000664000175000017500000000045112516636315015075 0ustar olembolemb00000000000000include README.rst LICENSE run_tests.py include examples/dbf2sqlite recursive-include examples *.dbf *.py recursive-include testcases *.dbf *.FPT recursive-include docs *.bat recursive-include docs *.py recursive-include docs *.rst recursive-include docs Makefile include docs/_static/PLACEHOLDER dbfread-2.0.7/run_tests.py0000775000175000017500000000077312435673607015756 0ustar olembolemb00000000000000#!/usr/bin/env python """ Runs tests in Python 2 and 3. If you want tests to run before each commit you can install this as a pre-commit hook: ln -sf ../../run_tests.py .git/hooks/pre-commit The commit will now be canceled if tests fail. """ import os import sys sys.path.insert(0, '.') def print_yellow(message): print('\033[0;33m{}\033[0m'.format(message)) print_yellow('Python 2') if os.system('py.test'): sys.exit(1) print_yellow('Python 3') if os.system('py.test-3'): sys.exit(1) dbfread-2.0.7/LICENSE0000664000175000017500000000213412435673607014351 0ustar olembolemb00000000000000The MIT License Copyright (c) Ole Martin Bjørndalen / UiT The Arctic University of Norway Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. dbfread-2.0.7/PKG-INFO0000664000175000017500000001020613016017614014422 0ustar olembolemb00000000000000Metadata-Version: 1.0 Name: dbfread Version: 2.0.7 Summary: Read DBF Files with Python Home-page: https://dbfread.readthedocs.io/ Author: Ole Martin Bjorndalen Author-email: ombdalen@gmail.com License: MIT Description: dbfread - Read DBF Files with Python ==================================== DBF is a file format used by databases such dBase, Visual FoxPro, and FoxBase+. This library reads DBF files and returns the data as native Python data types for further processing. It is primarily intended for batch jobs and one-off scripts. :: >>> from dbfread import DBF >>> for record in DBF('people.dbf'): ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) By default records are streamed directly from the file. If you have enough memory you can instead load them into a list. This allows for random access:: >>> table = DBF('people.dbf', load=True) >>> print(table.records[1]['NAME']) Bob >>> print(table.records[0]['NAME']) Alice Full documentation at https://dbfread.readthedocs.io/ See docs/changes.rst for a full list of changes in each version. Main Features ------------- * written for Python 3, but also works in 2.7 * simple but flexible API * data is returned as native Python data types * records are ordered dictionaries, but can be reconfigured to be of any type * aims to handle all variants of DBF files. (Currently only widely tested with Visual FoxPro, but should work well with other variants.) * support for 18 field types. Custom types can be added by subclassing ``FieldParser`` * reads ``FPT`` and ``DBT`` memo files, both text and binary data * handles mixed case file names gracefully on case sensitive file systems * can retrieve deleted records Installing ---------- Requires Python 3.2 or 2.7. :: pip install dbfread ``dbfread`` is a pure Python module and doesn't depend on any packages outside the standard library. To build documentation locally:: python setup.py docs This requires Sphinx. The resulting files can be found in ``docs/_build/``. Source code ------------ Latest stable release: http://github.com/olemb/dbfread/ Development version: http://github.com/olemb/dbfread/tree/develop/ API Changes ----------- ``dbfread.open()`` and ``dbfread.read()`` are deprecated as of version ``2.0``, and will be removed in ``2.2``. The ``DBF`` class is no longer a subclass of ``list``. This makes the API a lot cleaner and easier to understand, but old code that relied on this behaviour will be broken. Iteration and record counting works the same as before. Other list operations can be rewritten using the ``record`` attribute. For example:: table = dbfread.read('people.dbf') print(table[1]) can be rewritten as:: table = DBF('people.dbf', load=True) print(table.records[1]) ``open()`` and ``read()`` both return ``DeprecatedDBF``, which is a subclass of ``DBF`` and ``list`` and thus backward compatible. License ------- dbfread is released under the terms of the `MIT license `_. Contact ------- Ole Martin Bjorndalen - ombdalen@gmail.com Platform: UNKNOWN dbfread-2.0.7/README.rst0000664000175000017500000000573012725544010015023 0ustar olembolemb00000000000000dbfread - Read DBF Files with Python ==================================== DBF is a file format used by databases such dBase, Visual FoxPro, and FoxBase+. This library reads DBF files and returns the data as native Python data types for further processing. It is primarily intended for batch jobs and one-off scripts. :: >>> from dbfread import DBF >>> for record in DBF('people.dbf'): ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) By default records are streamed directly from the file. If you have enough memory you can instead load them into a list. This allows for random access:: >>> table = DBF('people.dbf', load=True) >>> print(table.records[1]['NAME']) Bob >>> print(table.records[0]['NAME']) Alice Full documentation at https://dbfread.readthedocs.io/ See docs/changes.rst for a full list of changes in each version. Main Features ------------- * written for Python 3, but also works in 2.7 * simple but flexible API * data is returned as native Python data types * records are ordered dictionaries, but can be reconfigured to be of any type * aims to handle all variants of DBF files. (Currently only widely tested with Visual FoxPro, but should work well with other variants.) * support for 18 field types. Custom types can be added by subclassing ``FieldParser`` * reads ``FPT`` and ``DBT`` memo files, both text and binary data * handles mixed case file names gracefully on case sensitive file systems * can retrieve deleted records Installing ---------- Requires Python 3.2 or 2.7. :: pip install dbfread ``dbfread`` is a pure Python module and doesn't depend on any packages outside the standard library. To build documentation locally:: python setup.py docs This requires Sphinx. The resulting files can be found in ``docs/_build/``. Source code ------------ Latest stable release: http://github.com/olemb/dbfread/ Development version: http://github.com/olemb/dbfread/tree/develop/ API Changes ----------- ``dbfread.open()`` and ``dbfread.read()`` are deprecated as of version ``2.0``, and will be removed in ``2.2``. The ``DBF`` class is no longer a subclass of ``list``. This makes the API a lot cleaner and easier to understand, but old code that relied on this behaviour will be broken. Iteration and record counting works the same as before. Other list operations can be rewritten using the ``record`` attribute. For example:: table = dbfread.read('people.dbf') print(table[1]) can be rewritten as:: table = DBF('people.dbf', load=True) print(table.records[1]) ``open()`` and ``read()`` both return ``DeprecatedDBF``, which is a subclass of ``DBF`` and ``list`` and thus backward compatible. License ------- dbfread is released under the terms of the `MIT license `_. Contact ------- Ole Martin Bjorndalen - ombdalen@gmail.com dbfread-2.0.7/dbfread.egg-info/0000775000175000017500000000000013016017614016407 5ustar olembolemb00000000000000dbfread-2.0.7/dbfread.egg-info/zip-safe0000664000175000017500000000000112725543572020054 0ustar olembolemb00000000000000 dbfread-2.0.7/dbfread.egg-info/top_level.txt0000664000175000017500000000001013016017614021130 0ustar olembolemb00000000000000dbfread dbfread-2.0.7/dbfread.egg-info/SOURCES.txt0000664000175000017500000000235713016017614020302 0ustar olembolemb00000000000000LICENSE MANIFEST.in README.rst run_tests.py setup.cfg setup.py dbfread/__init__.py dbfread/codepages.py dbfread/dbf.py dbfread/dbversions.py dbfread/deprecated_dbf.py dbfread/exceptions.py dbfread/field_parser.py dbfread/ifiles.py dbfread/memo.py dbfread/struct_parser.py dbfread/test_field_parser.py dbfread/test_ifiles.py dbfread/test_invalid_value.py dbfread/test_memo.py dbfread/test_read_and_length.py dbfread/version.py dbfread.egg-info/PKG-INFO dbfread.egg-info/SOURCES.txt dbfread.egg-info/dependency_links.txt dbfread.egg-info/top_level.txt dbfread.egg-info/zip-safe docs/Makefile docs/acknowledgements.rst docs/api_changes.rst docs/changes.rst docs/conf.py docs/dbf_objects.rst docs/exporting_data.rst docs/field_types.rst docs/index.rst docs/installing.rst docs/introduction.rst docs/license.rst docs/make.bat docs/resources.rst docs/_static/PLACEHOLDER examples/custom_field_type.py examples/dbf2sqlite examples/dbfinfo.py examples/namedtuples.py examples/pandas_dataframe.py examples/print_csv.py examples/print_invalid_values.py examples/record_objects.py examples/using_dataset.py examples/files/invalid_value.dbf examples/files/make_example_files.py examples/files/people.dbf testcases/memotest.FPT testcases/memotest.dbf testcases/no_memofile.dbfdbfread-2.0.7/dbfread.egg-info/dependency_links.txt0000664000175000017500000000000113016017614022455 0ustar olembolemb00000000000000 dbfread-2.0.7/dbfread.egg-info/PKG-INFO0000664000175000017500000001020613016017614017503 0ustar olembolemb00000000000000Metadata-Version: 1.0 Name: dbfread Version: 2.0.7 Summary: Read DBF Files with Python Home-page: https://dbfread.readthedocs.io/ Author: Ole Martin Bjorndalen Author-email: ombdalen@gmail.com License: MIT Description: dbfread - Read DBF Files with Python ==================================== DBF is a file format used by databases such dBase, Visual FoxPro, and FoxBase+. This library reads DBF files and returns the data as native Python data types for further processing. It is primarily intended for batch jobs and one-off scripts. :: >>> from dbfread import DBF >>> for record in DBF('people.dbf'): ... print(record) OrderedDict([('NAME', 'Alice'), ('BIRTHDATE', datetime.date(1987, 3, 1))]) OrderedDict([('NAME', 'Bob'), ('BIRTHDATE', datetime.date(1980, 11, 12))]) By default records are streamed directly from the file. If you have enough memory you can instead load them into a list. This allows for random access:: >>> table = DBF('people.dbf', load=True) >>> print(table.records[1]['NAME']) Bob >>> print(table.records[0]['NAME']) Alice Full documentation at https://dbfread.readthedocs.io/ See docs/changes.rst for a full list of changes in each version. Main Features ------------- * written for Python 3, but also works in 2.7 * simple but flexible API * data is returned as native Python data types * records are ordered dictionaries, but can be reconfigured to be of any type * aims to handle all variants of DBF files. (Currently only widely tested with Visual FoxPro, but should work well with other variants.) * support for 18 field types. Custom types can be added by subclassing ``FieldParser`` * reads ``FPT`` and ``DBT`` memo files, both text and binary data * handles mixed case file names gracefully on case sensitive file systems * can retrieve deleted records Installing ---------- Requires Python 3.2 or 2.7. :: pip install dbfread ``dbfread`` is a pure Python module and doesn't depend on any packages outside the standard library. To build documentation locally:: python setup.py docs This requires Sphinx. The resulting files can be found in ``docs/_build/``. Source code ------------ Latest stable release: http://github.com/olemb/dbfread/ Development version: http://github.com/olemb/dbfread/tree/develop/ API Changes ----------- ``dbfread.open()`` and ``dbfread.read()`` are deprecated as of version ``2.0``, and will be removed in ``2.2``. The ``DBF`` class is no longer a subclass of ``list``. This makes the API a lot cleaner and easier to understand, but old code that relied on this behaviour will be broken. Iteration and record counting works the same as before. Other list operations can be rewritten using the ``record`` attribute. For example:: table = dbfread.read('people.dbf') print(table[1]) can be rewritten as:: table = DBF('people.dbf', load=True) print(table.records[1]) ``open()`` and ``read()`` both return ``DeprecatedDBF``, which is a subclass of ``DBF`` and ``list`` and thus backward compatible. License ------- dbfread is released under the terms of the `MIT license `_. Contact ------- Ole Martin Bjorndalen - ombdalen@gmail.com Platform: UNKNOWN dbfread-2.0.7/setup.cfg0000664000175000017500000000022013016017614015141 0ustar olembolemb00000000000000[wheel] universal = 1 [easy_install] [pytest] norecursedirs = build dist examples [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 dbfread-2.0.7/examples/0000775000175000017500000000000013016017614015144 5ustar olembolemb00000000000000dbfread-2.0.7/examples/custom_field_type.py0000664000175000017500000000054212516636315021246 0ustar olembolemb00000000000000""" Add custom field parsing by subclassing FieldParser. """ from dbfread import DBF, FieldParser class CustomFieldParser(FieldParser): def parseC(self, field, data): # Return strings reversed. return data.rstrip(b' 0').decode()[::-1] for record in DBF('files/people.dbf', parserclass=CustomFieldParser): print(record['NAME']) dbfread-2.0.7/examples/using_dataset.py0000664000175000017500000000067512725544010020361 0ustar olembolemb00000000000000""" Convert a DBF file to an SQLite table. Requires dataset: https://dataset.readthedocs.io/ """ import dataset from dbfread import DBF # Change to "dataset.connect('people.sqlite')" if you want a file. db = dataset.connect('sqlite:///:memory:') table = db['people'] for record in DBF('files/people.dbf', lowernames=True): table.insert(record) # Select and print a record just to show that it worked. print(table.find_one(name='Alice')) dbfread-2.0.7/examples/dbf2sqlite0000775000175000017500000000605113015624542017136 0ustar olembolemb00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ dbf2sqlite - convert dbf files into sqlite database Ole Martin Bjørndalen University of Tromsø Todo: - -v --verbose option - handle existing table (-f option?) - primary key option? (make first column primary key) - create only option? - insert only option? - options to select columns to insert? """ import os import sys import argparse import sqlite3 import traceback from dbfread import DBF typemap = { 'F': 'FLOAT', 'L': 'BOOLEAN', 'I': 'INTEGER', 'C': 'TEXT', 'N': 'REAL', # because it can be integer or float 'M': 'TEXT', 'D': 'DATE', 'T': 'DATETIME', '0': 'INTEGER', } def add_table(cursor, table): """Add a dbase table to an open sqlite database.""" cursor.execute('drop table if exists %s' % table.name) field_types = {} for f in table.fields: field_types[f.name] = typemap.get(f.type, 'TEXT') # # Create the table # defs = ', '.join(['"%s" %s' % (f, field_types[f]) for f in table.field_names]) sql = 'create table "%s" (%s)' % (table.name, defs) cursor.execute(sql) # Create data rows refs = ', '.join([':' + f for f in table.field_names]) sql = 'insert into "%s" values (%s)' % (table.name, refs) for rec in table: cursor.execute(sql, list(rec.values())) def parse_args(): parser = argparse.ArgumentParser( description='usage: %prog [OPTIONS] table1.dbf ... tableN.dbf') arg = parser.add_argument arg('-o', '--output-file', action='store', dest='output_file', default=None, help='sqlite database to write to ' '(default is to print schema to stdout)') arg('-e', '--encoding', action='store', dest='encoding', default=None, help='character encoding in DBF file') arg('--char-decode-errors', action='store', dest='char_decode_errors', default='strict', help='how to handle decode errors (see pydoc bytes.decode)') arg('tables', metavar='TABLE', nargs='+', help='tables to add to sqlite database') return parser.parse_args() def main(): args = parse_args() conn = sqlite3.connect(args.output_file or ':memory:') cursor = conn.cursor() for table_file in args.tables: try: add_table(cursor, DBF(table_file, lowernames=True, encoding=args.encoding, char_decode_errors=args.char_decode_errors)) except UnicodeDecodeError as err: traceback.print_exc() sys.exit('Please use --encoding or --char-decode-errors.') conn.commit() # # Dump SQL schema and data to stdout if no # database file was specified. # # This currently only works in Python 3, # since Python 2 somehow defaults to 'ascii' # encoding. # if not args.output_file: for line in conn.iterdump(): print(line) if __name__ == '__main__': main() dbfread-2.0.7/examples/print_csv.py0000664000175000017500000000035012516636315017534 0ustar olembolemb00000000000000"""Export to CSV.""" import sys import csv from dbfread import DBF table = DBF('files/people.dbf') writer = csv.writer(sys.stdout) writer.writerow(table.field_names) for record in table: writer.writerow(list(record.values())) dbfread-2.0.7/examples/namedtuples.py0000664000175000017500000000067612435673607020066 0ustar olembolemb00000000000000""" Return records as named tuples. This saves a lot of memory. """ from collections import namedtuple from dbfread import DBF table = DBF('files/people.dbf', lowernames=True) # Set record factory. This must be done after # the table is opened because it needs the field # names. Record = namedtuple('Record', table.field_names) factory = lambda lst: Record(**dict(lst)) table.recfactory = factory for record in table: print(record.name) dbfread-2.0.7/examples/print_invalid_values.py0000664000175000017500000000114712435673607021760 0ustar olembolemb00000000000000""" A field parser that returns invalid values as InvalidValue objects instead of raising ValueError. """ from dbfread import DBF, FieldParser, InvalidValue class MyFieldParser(FieldParser): def parse(self, field, data): try: return FieldParser.parse(self, field, data) except ValueError: return InvalidValue(data) table = DBF('files/invalid_value.dbf', parserclass=MyFieldParser) for i, record in enumerate(table): for name, value in record.items(): if isinstance(value, InvalidValue): print('records[{}][{!r}] == {!r}'.format(i, name, value)) dbfread-2.0.7/examples/dbfinfo.py0000775000175000017500000000146112435673607017150 0ustar olembolemb00000000000000#!/usr/bin/env python from __future__ import print_function import sys from dbfread import DBF def show(*words): print(' ' + ' '.join(str(word) for word in words)) def show_field(field): print(' {} ({} {})'.format(field.name, field.type, field.length)) def main(): for filename in sys.argv[1:]: print(filename + ':') table = DBF(filename, ignore_missing_memofile=True) show('Name:', table.name) show('Memo File:', table.memofilename or '') show('DB Version:', table.dbversion) show('Records:', len(table)) show('Deleted Records:', len(table.deleted)) show('Last Updated:', table.date) show('Character Encoding:', table.encoding) show('Fields:') for field in table.fields: show_field(field) main() dbfread-2.0.7/examples/files/0000775000175000017500000000000013016017614016246 5ustar olembolemb00000000000000dbfread-2.0.7/examples/files/make_example_files.py0000775000175000017500000000321212435673607022451 0ustar olembolemb00000000000000#!/usr/bin/env python2 """ This creates the example file people.dbf. You need the dbfpy library to run this. """ from __future__ import print_function from dbfpy import dbf def make_example_file(filename, fields, records, delete_last_record=False): field_names = [field[0] for field in fields] print('Creating', filename) print(' Fields:', ', '.join(field_names)) print(' ', len(records), 'records') db = dbf.Dbf(filename, new=True) db.addField(*fields) for data in records: record = db.newRecord() for name, value in zip(field_names, data): record[name] = value record.store() if delete_last_record: # Delete the last one of those records. record.delete() record.store() try: db.close() except AttributeError: # This ignores the following error: # self.memo.flush() # AttributeError: 'NoneType' object has no attribute 'flush' pass make_example_file('people.dbf', [('NAME', 'C', 16), ('BIRTHDATE', 'D')], [('Alice', (1987, 3, 1)), ('Bob', (1980, 11, 12)), ('Deleted Guy', (1979, 12, 22))], delete_last_record=True) make_example_file('../../testcases/memotest.dbf', [('NAME', 'C', 16), ('BIRTHDATE', 'D'), ('MEMO', 'M')], [('Alice', (1987, 3, 1), 'Alice memo'), ('Bob', (1980, 11, 12), 'Bob memo'), ('Deleted Guy', (1979, 12, 22), 'Deleted Guy memo')], delete_last_record=True) dbfread-2.0.7/examples/files/people.dbf0000664000175000017500000000025512435673607020227 0ustar olembolemb00000000000000raNAMECBIRTHDATED Alice 19870301 Bob 19801112*Deleted Guy 19791222dbfread-2.0.7/examples/files/invalid_value.dbf0000664000175000017500000000025512435673607021565 0ustar olembolemb00000000000000raNAMECBIRTHDATED Alice NotAYear Bob 19801112*Deleted Guy 19791222dbfread-2.0.7/examples/record_objects.py0000664000175000017500000000057612435673607020533 0ustar olembolemb00000000000000""" Return records as objects with fields as attributes. """ from __future__ import print_function from dbfread import DBF class Record(object): def __init__(self, items): for name, value in items: setattr(self, name, value) for record in DBF('files/people.dbf', recfactory=Record, lowernames=True): print(record.name, 'was born on', record.birthdate) dbfread-2.0.7/examples/pandas_dataframe.py0000664000175000017500000000042312725544010020770 0ustar olembolemb00000000000000""" Load content of a DBF file into a Pandas data frame. The iter() is required because Pandas doesn't detect that the DBF object is iterable. """ from dbfread import DBF from pandas import DataFrame dbf = DBF('files/people.dbf') frame = DataFrame(iter(dbf)) print(frame) dbfread-2.0.7/testcases/0000775000175000017500000000000013016017614015324 5ustar olembolemb00000000000000dbfread-2.0.7/testcases/no_memofile.dbf0000664000175000017500000000074012435673607020311 0ustar olembolemb000000000000000rˆNAMECBIRTHDATEDMEMOM Alice 19870301 Bob 19801112*Deleted Guy 19791222dbfread-2.0.7/testcases/memotest.FPT0000664000175000017500000000500012435673607017545 0ustar olembolemb00000000000000 Alice memoBob memoDeleted Guy memoDeleted Guy memodbfread-2.0.7/testcases/memotest.dbf0000664000175000017500000000074012435673607017655 0ustar olembolemb000000000000000rˆNAMECBIRTHDATEDMEMOM Alice 19870301 Bob 19801112*Deleted Guy 19791222