diff --git a/.bzrignore b/.bzrignore index 9b46d9d..1f4abe3 100644 --- a/.bzrignore +++ b/.bzrignore @@ -13,3 +13,6 @@ fastimport.stream wheelhouse ./.git ./.gitignore +./venv +./python.bat +./*.html diff --git a/.gitignore b/.gitignore index 71e0229..1d106d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,14 @@ +*.egg-info *.pyc .bzr/* *.~* docs/manual.html docs/manual/.build/doctrees/* docs/manual/.build/html/* +venv/ +dist/ +.tox/ +MANIFEST +python.bat +NEWS.html +README.html diff --git a/AUTHORS.txt b/AUTHORS.rst similarity index 78% rename from AUTHORS.txt rename to AUTHORS.rst index 297e869..eb1efdd 100644 --- a/AUTHORS.txt +++ b/AUTHORS.rst @@ -17,7 +17,13 @@ Contributors: * Masayuki Takeda * Morgan McClure * Nathan P. Stien +* Piotr Korowacki +* Reis Baltaoglu +* Ryan Downing +* Scott Armitage * Stefan Schmitt +* Svein Seldal +* Theo Sbrissa * Zachary Clifford * "durexyl" @ GitHub * "erki1993" @ GitHub diff --git a/MANIFEST.in b/MANIFEST.in index 8c65766..97193ec 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include AUTHORS.txt +include AUTHORS.rst include LICENSE.txt -include NEWS.txt -include README.txt +include NEWS.rst +include README.rst diff --git a/Makefile b/Makefile index c67ec8d..cac3095 100644 --- a/Makefile +++ b/Makefile @@ -4,12 +4,13 @@ all: @echo Available targets: @echo clean - clean build directory @echo test - run unittest - @echo epydoc - run epydoc to create API documentation + @echo dev - install via pip as editable package + @echo apidoc - run epdoc to create API documentation @echo wininst - Windows installer for Python @echo docs - build docs with ReST and Sphinx @echo wheel - build python wheel binary archive -.PHONY: clean test epydoc wininst docs +.PHONY: clean test epydoc wininst docs dev apidoc wheel clean: $(PYTHON) setup.py clean -a @@ -17,15 +18,18 @@ clean: test: $(PYTHON) setup.py test -q -epydoc: - epydoc.py -o api -v intelhex +dev: + $(PYTHON) -m pip install -e . + +apidoc: + pdoc -o docs/api --html -f intelhex wininst: $(PYTHON) setup.py bdist_wininst -d. docs: - rst2html docs/manual.txt docs/manual.html - make -C docs/manual html + rst2html5.py docs/manual.txt docs/manual.html + $(MAKE) -C docs/manual html wheel: - $(PYTHON) -m pip wheel . + $(PYTHON) -m pip wheel -w dist . diff --git a/NEWS.rst b/NEWS.rst new file mode 100644 index 0000000..14e7e4b --- /dev/null +++ b/NEWS.rst @@ -0,0 +1,186 @@ +***************** +IntelHex releases +***************** + +* Added support for ``in`` operator to check if address is in HEX file. + +2.3.0 (2020-10-20) +------------------ +* Add ``IntelHex.find()`` method to find a given byte pattern. (Scott Armitage) +* API changes: ``IntelHex.segments()`` method supports new optional parameter + ``min_gap`` to allow consolidation of segments with small but existing gaps + into a single segment. Default value is 1. (Ryan Downing) +* API changes: ``IntelHex.tofile()`` now supports the optional ``byte_count`` + parameter from ``IntelHex.write_hex_file()``. Only used if ``format = hex``. + (Reis Baltaoglu) +* Fix Python 3.9 compatibility issue with 'array' module (Piotr Korowacki) +* Fix installation for Python version taking setup rather from setuptools than + distutils (Theo Sbrissa) + +2.2.1 (2018-01-30) +------------------ +* Fixes for PyPI. + +2.2 (2018-01-28) +---------------- +* API changes: ``IntelHex.write_hex_file`` method: added support for new + parameter: ``eolstyle = native | CRLF``. (Alexander Belchenko) +* API changes: ``IntelHex.write_hex_file()`` method gets new optional + parameter ``byte_count`` to specify how many bytes should be written + to each data record in output file. Default value is 16. + (patch from GitHub user erki1993) +* Unit tests: Fixed xrange overflow test for Python 2.7 on 64-bit platforms. + Use ``sys.maxint`` to ensure we trigger an exception. (Masayuki Takeda) +* Script ``hexinfo.py``: Python 3 compatibility for processing start address + dict keys. (patch from GitHub user mentaal) +* Added ``get_memory_size()`` method: approx memory footprint of IntelHex object + plus data. (Alexander Belchenko) +* Better compatibility with Python 3. (Alexander Belchenko) + +2.1 (2016-03-31) +---------------- +* API changes: added ``IntelHex.segments()`` method that returns + a list of ordered tuple objects, representing contiguous occupied data + addresses. (Andrew Fernandes) +* New command-line script ``hexinfo.py`` to print summary about hex files + contents (file name, start address, address ranges covered by the data) + in YAML format. (Andrew Fernandes) +* Better Python 3 compatibility when ``hex2bin.py`` and ``bin2hex.py`` + scripts are trying to read/write binary data from stdin or to stdout. + (GitHub issue https://github.com/python-intelhex/intelhex/issues/4) +* The main activity of the IntelHex project slowly drifting towards + GitHub - the main social network for OSS developers. + I'd really like to get some help from additional maintainer though. +* API changes: ``IntelHex.dump()`` method gets new optional parameters: + ``width``, ``withpadding`` to control generation of output text. + (patch from GitHub user durexyl) +* Script ``hex2dump.py`` gets new option ``--width`` to support + corresponding parameter in ``IntelHex.dump()`` method. + +2.0 (2015-04-12) +---------------- +* The same codebase can be run on both Python 2 (2.4-2.7) + and Python 3 (3.2+). No need to use 2to3. +* ``compat.py``: provide more helper functions and aliases to reduce changes + required to convert python 2 compatible sources to python 3. + The code becomes quite ugly, but such compatibility has its price. +* Python 3 compatibility: tobinstr should return bytes not unicode string + (Bug #1212698). +* Python 2: better support for long int addresses (over 2GB) + (Bug #1408934) + +1.5 (2013-08-02) +---------------- +* API changes: Functions tobinarray/tobinstr/tobinfile: + pad parameter is deprecated and will be removed in + future releases. Use IntelHex.padding attribute instead, + and don't pass pad as None explicitly please. + If you need to use size parameter, then use syntax like that: + ``ih.tobinarray(start=xxx, size=yyy)`` +* API changes: Functions tobinarray/tobinstr/tobinfile: + default value of pad is None now (was ``0xFF``) + to allow using value of ``IntelHex.padding`` + if no explicit pad specified. +* Fixed bug: wrong ``getopt`` error handling in some scripts. + (Thanks to Andy Mozhevilov for bug report) +* PEP-8 style improvements. (Thanks to Stefan Schmitt) +* ``IntelHex16bit.tobinarray`` method returns array of unsigned short + (words) values. (Feature request from Stefan Schmitt) +* Improved Python 3 compatibility (don't use old file() function). + (Thanks to Luis Panadero Guardeño for bug report) + +1.4 (2012-04-25) +---------------- +* New feature: compare 2 hex files using hex dump + as string representation. Feature available as + worker function diff_dumps() and as command-line + utility hexdiff.py (#627924). +* Changes in the codebase suggested by 2to3 tool to provide + compatibility with Python3. Now sources can be successfully + converted to Python3 with 2to3 utility. + See Python 3 notes in README.txt and documentation. + (Thanks to Bernhard Leiner for his help) +* Fixed bug #988148: ``IntelHex16bit`` should copy all public attributes + from source IntelHex 8-bit object. (Thanks to Morgan McClure) + +1.3 (2010-11-24) +---------------- +* ``hex2dump``: show 0x7F character as dot for better compatibility + with GNU less utility. +* tobinarray, tobinfile, tobinstr: added size parameter. (Bug #408748) +* fixed error in ``hexmerge.py`` script. (#676023) + +1.2 (2009-08-04) +---------------- +* Fixed bug 372620: tobinarray on empty file should return pad bytes + when address range explicitly specified. +* Improved docstrings: explicitly say that ``end`` param of to-* methods + is always inclusive. (see bug #372625 for details). +* Improved documentation on ``ih.dump(tofile)``. + +1.1 (2009-03-12) +---------------- +* Fixed bug in writing hex files with small chains of bytes +* Improved Python 2.6 compatibility + +1.0 (2009-01-01) +---------------- +* Improved API, better performance +* New User Manual (Zachary Clifford) + +0.9 (2007-06-16) +---------------- +New API release. + +* New API +* Performance improvements: read hex file now ~45% faster + +0.8.6 (2007-04-27) +------------------ +Bug fixes and performance improvements. + +* ``IntelHex`` is able to read/write start address records + (HEX record type ``03`` and ``05``). (fix bug #109872) +* Backport (from 0.9 branch) of performance improvements + for reading hex files + +0.8.5 (2007-02-26) +------------------ +BugFix Release. + +Performance improvements for writing big hex files +when starting address is far from 0. Patch from Heiko Henkelmann. + +0.8.4 (2007-02-26) +------------------ +License added. + +The code is actually licensed under BSD, but there was +no LICENSE file in sources archive. Added license file +and explicit declaration in the source code. + +0.8.3 (2006-09-05) +------------------ +BugFix Release. + +Fix writing hex files with extended linear records +(when address overlaps 64K boundary). Patch from Henrik Maier. + +0.8.2 (2006-04-11) +------------------ +Major improvements release. + +* Introduced new class ``IntelHex16bit`` for manipulate data as 16-bit values +* You can manipulate data using dictionary-like interface + (i.e. syntax like: ``ih[addr] = value``) +* Added new method ``writefile(file)`` for writing data to hex file +* Using unittest for testing functionality + +0.6 (2006-03) +------------- +Convertor engine ``hex2bin`` extracted to stand-alone function +for using by external clients of intelhex. + +0.5 (2005) +---------- +First public release. diff --git a/News.txt b/News.txt deleted file mode 100644 index 997cced..0000000 --- a/News.txt +++ /dev/null @@ -1,162 +0,0 @@ -NEWS -==== - -* 2018/01/28 v.2.2 - - - API changes: ``IntelHex.write_hex_file`` method: added support for new - parameter: ``eolstyle = native | CRLF``. (Alexander Belchenko) - - API changes: ``IntelHex.write_hex_file()`` method gets new optional - parameter ``byte_count`` to specify how many bytes should be written - to each data record in output file. Default value is 16. - (patch from GitHub user erki1993) - - Unit tests: Fixed xrange overflow test for Python 2.7 on 64-bit platforms. - Use sys.maxint to ensure we trigger an exception. (Masayuki Takeda) - - Script ``hexinfo.py``: Python 3 compatibility for processing start address - dict keys. (patch from GitHub user mentaal) - - Added get_memory_size() method: approx memory footprint of IntelHex object - plus data. (Alexander Belchenko) - - Better compatibility with Python 3. (Alexander Belchenko) - -* 2016/03/31 v.2.1 - - - API changes: added ``IntelHex.segments()`` method that returns - a list of ordered tuple objects, representing contiguous occupied data - addresses. (Andrew Fernandes) - - New command-line script ``hexinfo.py`` to print summary about hex files - contents (file name, start address, address ranges covered by the data) - in YAML format. (Andrew Fernandes) - - Better Python 3 compatibility when ``hex2bin.py`` and ``bin2hex.py`` - scripts are trying to read/write binary data from stdin or to stdout. - (GitHub issue https://github.com/bialix/intelhex/issues/4) - - The main activity of the IntelHex project slowly drifting towards - GitHub - the main social network for OSS developers. - I'd really like to get some help from additional maintainer though. - - API changes: ``IntelHex.dump()`` method gets new optional parameters: - ``width``, ``withpadding`` to control generation of output text. - (patch from GitHub user durexyl) - - Script ``hex2dump.py`` gets new option ``--width`` to support - corresponding parameter in ``IntelHex.dump()`` method. - -* 2015/04/12 v.2.0 - - - The same codebase can be run on both Python 2 (2.4-2.7) - and Python 3 (3.2+). No need to use 2to3. - - compat.py: provide more helper functions and aliases to reduce changes - required to convert python 2 compatible sources to python 3. - The code becomes quite ugly, but such compatibility has its price. - - Python 3 compatibility: tobinstr should return bytes not unicode string - (Bug #1212698). - - Python 2: better support for long int addresses (over 2GB) - (Bug #1408934) - -* 2013/08/02 v.1.5 - - - API changes: Functions tobinarray/tobinstr/tobinfile: - pad parameter is deprecated and will be removed in - future releases. Use IntelHex.padding attribute instead, - and don't pass pad as None explicitly please. - If you need to use size parameter, then use syntax like that: - ih.tobinarray(start=xxx, size=yyy) - - API changes: Functions tobinarray/tobinstr/tobinfile: - default value of pad is None now (was 0xFF) - to allow value of IntelHex.padding used - if no explicit pad specified. - - Fixed bug: wrong getopt error handling in some scripts. - (Thanks to Andy Mozhevilov for bug report) - - PEP-8 style improvements. (Thanks to Stefan Schmitt) - - IntelHex16bit.tobinarray method returns array of unsigned short - (words) values. (Feature request from Stefan Schmitt) - - Improved Python 3 compatibility (don't use old file() function). - (Thanks to Luis Panadero Guardeño for bug report) - -* 2012/04/25 v.1.4 - - - New feature: compare 2 hex files using hex dump - as string representation. Feature available as - worker function diff_dumps() and as command-line - utility hexdiff.py (#627924). - - Changes in the codebase suggested by 2to3 tool to provide - compatibility with Python3. Now sources can be successfully - converted to Python3 with 2to3 utility. - See Python 3 notes in README.txt and documentation. - (Thanks to Bernhard Leiner for his help) - - Fixed bug #988148: IntelHex16bit should copy all public attributes - from source IntelHex 8-bit object. (Thanks to Morgan McClure) - -* 2010/11/24 v.1.3 - - - hex2dump: show 0x7F character as dot for better compatibility - with GNU less utility. - - tobinarray, tobinfile, tobinstr: added size parameter. (Bug #408748) - - fixed error in hexmerge.py script. (#676023) - -* 2009/08/04 v.1.2 - - - Fixed bug 372620: tobinarray on empty file should return pad bytes - when address range explicitly specified. - - Improved docstrings: explicitly say that 'end' param of to-* methods - is always inclusive. (see bug #372625 for details). - - Improved documentation on ih.dump(tofile). - -* 2009/03/12 v.1.1 - - - Fixed bug in writing hex files with small chains of bytes - - Improved Python 2.6 compatibility - -* 2009/01/01 v.1.0 - - - Improved API, better performance - - New User Manual (Zachary Clifford) - -* 2007/06/16 v.0.9.0 (New API release) - - - New API for IntelHex16bit - -* 2007/04/27 v.0.8.6 (BugFix and Performance improvements) - - - IntelHex is able to read/write start address records - (HEX record type 03 and 05). (fix bug #109872) - - Backport (from 0.9 branch) of performance improvements - for reading hex files - -* 2007/04/22 v.0.9.devel (New API pre-release) - - - New API - - Performance improvements: read hex file now ~45% faster - -* 2007/02/26 v.0.8.5 (BugFix Release) - - Performance improvements for writing big hex files - when starting address is far from 0. - Patch from Heiko Henkelmann. - -* 2007/02/26 v.0.8.4 (License added) - - The code actually licensed under BSD, but there was - no LICENSE file in sources archive. Added license file - and explicit declaration in the source code. - -* 2006/09/05 v.0.8.3 (BugFix Release) - - Fix writing hex files with extended linear records - (when address overlaps 64K boundary). - Patch from Henrik Maier. - -* 2006/04/11 v.0.8.2 (Major improvements release) - - - Introduced new class IntelHex16bit for manipulate - data as 16-bit values - - You can manipulate data using dictionary-like - interface (i.e. syntax: ih[addr] = value) - - Added new method .writefile(file) for writing - data to hex file - - Using unittest for testing functionality - -* 2006/03 v.0.6 - - Convertor engine hex2bin extracted to stand-alone function - for using by external clients of intelhex - -* 2005 v.0.5 - - First public release diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..00239ba --- /dev/null +++ b/README.rst @@ -0,0 +1,56 @@ +Python IntelHex library +*********************** + +Introduction +------------ +The Intel HEX file format is widely used in microprocessors and microcontrollers +area (embedded systems etc) as the de facto standard +for representation of code to be programmed into microelectronic devices. + +This work implements an ``intelhex`` Python library to read, write, +create from scratch and manipulate data from Intel HEX file format. + +The distribution package also includes several convenience Python scripts, +including "classic" ``hex2bin`` and ``bin2hex`` converters and more, +those based on the library itself. Check the docs to know more. + +License +------- +The code is distributed under BSD license, +see `LICENSE.txt `_. + +In short: you can use IntelHex library in your project without *any* +restrictions. + +Supported Python versions +------------------------- +IntelHex library supports Python 3 (3.5 or later) only. The 2.2.1 release was +the last one which has been checked against Python 2.7 and Python 3 until 3.5. + +Install +------- +Install using ``pip`` (recommended, no separate download required):: + + pip install intelhex + +Download +-------- +* https://pypi.org/project/IntelHex/ +* https://github.com/python-intelhex/intelhex/releases + +Source code, bug reports, patches +--------------------------------- +IntelHex on GitHub: + +https://github.com/python-intelhex/intelhex + +User manual +----------- +User manual for IntelHex is available in the sources ``docs/manual/`` directory. +You can browse User Manual online: + +https://readthedocs.org/projects/python-intelhex/ + +Changelog +--------- +See `NEWS.rst `_ diff --git a/Readme.txt b/Readme.txt deleted file mode 100644 index 21d7a5c..0000000 --- a/Readme.txt +++ /dev/null @@ -1,81 +0,0 @@ ------------------------ -Python IntelHex library ------------------------ - -Author: Alexander Belchenko -Contact: alexander belchenko at gmail com -Date: 2018/01/28 -Version: 2.2 - -Introduction ------------- -The Intel HEX file format is widely used in microprocessors and microcontrollers -area as the de facto standard for code representation for microelectronic devices programming. - -This work implements an **intelhex** Python library to read, write, -create from scratch and manipulate data from HEX (also known as Intel HEX) -file format. These operations are provided by ``IntelHex`` class. - -The distribution package also includes several convenience Python scripts -to do basic tasks that utilize this library. The ``bin2hex.py`` script -converts binary data to HEX, and the ``hex2bin.py`` works the other direction. -``hex2dump.py`` converts data from HEX to a hexdump which is useful for -inspecting data, and ``hexmerge.py`` merges multiple HEX files into one. - -License -------- -The code is distributed under BSD license. See LICENSE.txt in the sources archive. - -In short: you can use IntelHex library in your project without any restrictions. - -If you're using IntelHex library in your open-source project, or your company -created freely available set of tools, utilities or sdk based on IntelHex -library, please send me an email and tell something about your project. -I'd like to add the name of your project/company to page "Who Uses IntelHex". - -Python 3 compatibility ----------------------- -Intelhex library supports Python 2 (2.4-2.7) and Python 3 (3.2-3.5 or later) -without external libraries or 2to3 tool from the same codebase. - -Download --------- -* https://pypi.python.org/pypi/IntelHex -* https://github.com/bialix/intelhex/releases - -Install -------- -Install using pip (no separate download required): - - pip install intelhex - -Install from sources (classic python's magic spell): - - python setup.py install - -Source code ------------ -IntelHex on GitHub: - - https://github.com/bialix/intelhex - -Get code with git: - - git clone https://github.com/bialix/intelhex.git - -User manual ------------ -User manual for IntelHex is available in the sources docs/manual/ directory. -You can browse User Manual online: - - http://pythonhosted.org/IntelHex/ - - https://readthedocs.org/projects/python-intelhex/ - -API documentation ------------------ -You can use epydoc for creating API documentation for IntelHex class. Run: - - $ python epydoc.py intelhex - -epydoc tool: http://epydoc.sourceforge.net/ diff --git a/docs/manual.txt b/docs/manual.txt index 527549b..2935871 100644 --- a/docs/manual.txt +++ b/docs/manual.txt @@ -2,7 +2,7 @@ Python IntelHex Library User Manual ----------------------------------- -:Version: 2.2 +:Version: 2.3.0 .. contents:: .. sectnum:: diff --git a/docs/manual/part1-1.txt b/docs/manual/part1-1.txt index e18d64b..673fb69 100644 --- a/docs/manual/part1-1.txt +++ b/docs/manual/part1-1.txt @@ -21,7 +21,7 @@ https://pypi.python.org/pypi/IntelHex on GitHub: -https://github.com/bialix/intelhex +https://github.com/python-intelhex/intelhex Motivation diff --git a/docs/manual/part1-3.txt b/docs/manual/part1-3.txt index a07a412..6a9796c 100644 --- a/docs/manual/part1-3.txt +++ b/docs/manual/part1-3.txt @@ -39,11 +39,11 @@ https://pypi.python.org/pypi/IntelHex You can get the archive with the released code from corresponding section of GitHub project: -https://github.com/bialix/intelhex/releases +https://github.com/python-intelhex/intelhex/releases or even unreleased bleeding edge code from GitHub page: -https://github.com/bialix/intelhex +https://github.com/python-intelhex/intelhex Use the corresponding menu item in the right-hand side bar on that page (e.g. "Download ZIP"). @@ -51,7 +51,7 @@ Use the corresponding menu item in the right-hand side bar on that page Get source code with git ~~~~~~~~~~~~~~~~~~~~~~~~ - git clone https://github.com/bialix/intelhex.git + git clone https://github.com/python-intelhex/intelhex.git Install from sources diff --git a/intelhex/__init__.py b/intelhex/__init__.py index a631b0a..155f17f 100644 --- a/intelhex/__init__.py +++ b/intelhex/__init__.py @@ -445,6 +445,15 @@ def maxaddr(self): else: return max(aa) + def __contains__(self, addr): + ''' Returns a boolean if the address is present. + @param addr address of byte. + @return bool if address exists in HEX file. + ''' + if addr < 0: + raise TypeError('Address should be >= 0.') + return addr in self._buf + def __getitem__(self, addr): ''' Get requested byte from address. @param addr address of byte. @@ -455,14 +464,17 @@ def __getitem__(self, addr): if t in IntTypes: if addr < 0: raise TypeError('Address should be >= 0.') + addresses = dict_keys(self._buf) + if not addresses or addr > max(addresses): + raise IndexError return self._buf.get(addr, self.padding) elif t == slice: addresses = dict_keys(self._buf) ih = IntelHex() if addresses: addresses.sort() - start = addr.start or addresses[0] - stop = addr.stop or (addresses[-1]+1) + start = addr.start if addr.start is not None else addresses[0] + stop = addr.stop if addr.stop is not None else addresses[-1] + 1 step = addr.step or 1 for i in range_g(start, stop, step): x = self._buf.get(i) @@ -709,14 +721,15 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count if fclose: fclose() - def tofile(self, fobj, format): + def tofile(self, fobj, format, byte_count=16): """Write data to hex or bin file. Preferred method over tobin or tohex. @param fobj file name or file-like object @param format file format ("hex" or "bin") + @param byte_count bytes per line """ if format == 'hex': - self.write_hex_file(fobj) + self.write_hex_file(fobj, byte_count=byte_count) elif format == 'bin': self.tobinfile(fobj) else: @@ -764,6 +777,22 @@ def putsz(self, addr, s): self.puts(addr, s) self._buf[addr+len(s)] = 0 + def find(self, sub, start=None, end=None): + """Return the lowest index in self[start:end] where subsection sub is found. + Optional arguments start and end are interpreted as in slice notation. + + @param sub bytes-like subsection to find + @param start start of section to search within (optional) + @param end end of section to search within (optional) + """ + sub = bytes(sub) + for start, end in self[slice(start,end)].segments(): + b = self.gets(start, end-start) + i = b.find(sub) + if i != -1: + return start+i + return -1 + def dump(self, tofile=None, width=16, withpadding=False): """Dump object content to specified file object or to stdout if None. Format is a hexdump with some header information at the beginning, @@ -792,7 +821,7 @@ def dump(self, tofile=None, width=16, withpadding=False): elif eip is None and cs is not None and ip is not None: tofile.write('CS = 0x%04X, IP = 0x%04X\n' % (cs, ip)) else: - tofile.write('start_addr = %r\n' % start_addr) + tofile.write('start_addr = %r\n' % self.start_addr) # actual data addresses = dict_keys(self._buf) if addresses: @@ -873,10 +902,11 @@ def merge(self, other, overlap='error'): elif overlap == 'replace': self.start_addr = other.start_addr - def segments(self): + def segments(self, min_gap=1): """Return a list of ordered tuple objects, representing contiguous occupied data addresses. Each tuple has a length of two and follows the semantics of the range and xrange objects. The second entry of the tuple is always an integer greater than the first entry. + @param min_gap the minimum gap size between data in order to separate the segments """ addresses = self.addresses() if not addresses: @@ -884,12 +914,12 @@ def segments(self): elif len(addresses) == 1: return([(addresses[0], addresses[0]+1)]) adjacent_differences = [(b - a) for (a, b) in zip(addresses[:-1], addresses[1:])] - breaks = [i for (i, x) in enumerate(adjacent_differences) if x > 1] + breaks = [i for (i, x) in enumerate(adjacent_differences) if x > min_gap] endings = [addresses[b] for b in breaks] endings.append(addresses[-1]) - beginings = [addresses[b+1] for b in breaks] - beginings.insert(0, addresses[0]) - return [(a, b+1) for (a, b) in zip(beginings, endings)] + beginnings = [addresses[b+1] for b in breaks] + beginnings.insert(0, addresses[0]) + return [(a, b+1) for (a, b) in zip(beginnings, endings)] def get_memory_size(self): """Returns the approximate memory footprint for data.""" diff --git a/intelhex/__version__.py b/intelhex/__version__.py index ba4eb31..3ad5f13 100644 --- a/intelhex/__version__.py +++ b/intelhex/__version__.py @@ -1,3 +1,3 @@ # IntelHex library version information -version_info = (2, 2) +version_info = (2, 3, 0) version_str = '.'.join([str(i) for i in version_info]) diff --git a/intelhex/bench.py b/intelhex/bench.py index df48447..7586e2c 100644 --- a/intelhex/bench.py +++ b/intelhex/bench.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # (c) Alexander Belchenko, 2007, 2009 # [2013/08] NOTE: This file is keeping for historical reasons. diff --git a/intelhex/compat.py b/intelhex/compat.py index 194cd5d..2a6bee6 100644 --- a/intelhex/compat.py +++ b/intelhex/compat.py @@ -57,7 +57,8 @@ def asstr(s): return s return s.decode('latin1') - array_tobytes = getattr(array.array, "tobytes", array.array.tostring) + # for python >= 3.2 use 'tobytes', otherwise 'tostring' + array_tobytes = array.array.tobytes if sys.version_info[1] >= 2 else array.array.tostring IntTypes = (int,) StrType = str diff --git a/intelhex/scripts/__init__.py b/intelhex/scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/bin2hex.py b/intelhex/scripts/bin2hex.py similarity index 98% rename from scripts/bin2hex.py rename to intelhex/scripts/bin2hex.py index bc8bbbf..6a2545a 100755 --- a/scripts/bin2hex.py +++ b/intelhex/scripts/bin2hex.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. @@ -35,9 +35,9 @@ '''Intel HEX file format bin2hex convertor utility.''' -VERSION = '2.2' +VERSION = '2.3.0' -if __name__ == '__main__': +def main(): import getopt import os import sys @@ -113,3 +113,6 @@ from intelhex import bin2hex sys.exit(bin2hex(fin, fout, offset)) + +if __name__ == '__main__': + main() diff --git a/scripts/hex2bin.py b/intelhex/scripts/hex2bin.py similarity index 98% rename from scripts/hex2bin.py rename to intelhex/scripts/hex2bin.py index 4e16d9e..9533837 100755 --- a/scripts/hex2bin.py +++ b/intelhex/scripts/hex2bin.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2005-2018 Alexander Belchenko # All rights reserved. @@ -35,9 +35,9 @@ '''Intel HEX file format hex2bin convertor utility.''' -VERSION = '2.2' +VERSION = '2.3.0' -if __name__ == '__main__': +def main(): import getopt import os import sys @@ -130,3 +130,6 @@ from intelhex import hex2bin sys.exit(hex2bin(fin, fout, start, end, size, pad)) + +if __name__ == '__main__': + main() diff --git a/scripts/hex2dump.py b/intelhex/scripts/hex2dump.py similarity index 99% rename from scripts/hex2dump.py rename to intelhex/scripts/hex2dump.py index 80b9f61..364fe1f 100755 --- a/scripts/hex2dump.py +++ b/intelhex/scripts/hex2dump.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. @@ -35,7 +35,7 @@ """Show content of hex file as hexdump.""" -VERSION = '2.2' +VERSION = '2.3.0' USAGE = '''hex2dump: show content of hex file as hexdump. Usage: diff --git a/scripts/hexdiff.py b/intelhex/scripts/hexdiff.py similarity index 98% rename from scripts/hexdiff.py rename to intelhex/scripts/hexdiff.py index 85ab3db..8acb58b 100755 --- a/scripts/hexdiff.py +++ b/intelhex/scripts/hexdiff.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2011-2018 Alexander Belchenko # All rights reserved. @@ -37,7 +37,7 @@ of compared data. """ -VERSION = '2.2' +VERSION = '2.3.0' USAGE = '''hexdiff: diff dumps of 2 hex files. Usage: diff --git a/scripts/hexinfo.py b/intelhex/scripts/hexinfo.py similarity index 97% rename from scripts/hexinfo.py rename to intelhex/scripts/hexinfo.py index 2940df4..e4b6374 100755 --- a/scripts/hexinfo.py +++ b/intelhex/scripts/hexinfo.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2015 Andrew Fernandes # All rights reserved. @@ -38,7 +38,7 @@ data (if any), in YAML format. """ -VERSION = '2.2' +VERSION = '2.3.0' USAGE = '''hexinfo: summarize a hex file's contents. Usage: @@ -61,7 +61,7 @@ def summarize_yaml(fname): if ih.start_addr: keys = sorted(ih.start_addr.keys()) if keys == ['CS','IP']: - entry = ih.start_addr['CS'] * 65536 + ih.start_addr['IP'] + entry = ih.start_addr['CS'] * 16 + ih.start_addr['IP'] elif keys == ['EIP']: entry = ih.start_addr['EIP'] else: diff --git a/scripts/hexmerge.py b/intelhex/scripts/hexmerge.py similarity index 99% rename from scripts/hexmerge.py rename to intelhex/scripts/hexmerge.py index fc6877e..8be98e8 100755 --- a/scripts/hexmerge.py +++ b/intelhex/scripts/hexmerge.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2008-2018 Alexander Belchenko # All rights reserved. @@ -35,7 +35,7 @@ """Merge content of several hex files into one file.""" -VERSION = '2.2' +VERSION = '2.3.0' USAGE = '''hexmerge: merge content of hex files. Usage: diff --git a/intelhex/test.py b/intelhex/test.py index e016e26..2aff575 100755 --- a/intelhex/test.py +++ b/intelhex/test.py @@ -612,11 +612,17 @@ def test_init_from_obj(self): self.assertNotEqual(id(ih), id(ih2)) def test_dict_interface(self): + # https://github.com/python-intelhex/intelhex/issues/54 + # uninitialized objects no longer return padding bytes for random + # addresses. Anything set defines an upper address, remaining bytes + # down to zero _will_ yield padding bytes then. ih = IntelHex() + ih[1] = 0 self.assertEqual(0xFF, ih[0]) # padding byte substitution ih[0] = 1 self.assertEqual(1, ih[0]) del ih[0] + del ih[1] self.assertEqual({}, ih.todict()) # padding byte substitution def test_len(self): @@ -627,14 +633,43 @@ def test_len(self): ih[1000] = 2 self.assertEqual(2, len(ih)) + def test_list(self): + # https://github.com/python-intelhex/intelhex/issues/54 + ih = IntelHex() + self.assertEqual([], list(ih)) + ih[2] = 1 + self.assertEqual([0xFF, 0xFF, 1], list(ih)) + + def test__contains__(self): + # https://github.com/python-intelhex/intelhex/issues/TBD + ih = IntelHex() + self.assertFalse(0 in ih) + ih[1] = 0xFF + self.assertTrue(1 in ih) + # big address + ih[2**32-1] = 1 + self.assertTrue((2**32-1) in ih) + # Invalid address + def contains(index): + return index in ih + self.assertRaisesMsg(TypeError, + 'Address should be >= 0.', + contains, -1) + def test__getitem__(self): ih = IntelHex() + # https://github.com/python-intelhex/intelhex/issues/54 + # uninitialized objects no longer return padding bytes for random + # addresses. Anything set defines an upper address, remaining bytes + # down to zero _will_ yield padding bytes then. # simple cases + ih[1] = 0xFF self.assertEqual(0xFF, ih[0]) ih[0] = 1 self.assertEqual(1, ih[0]) # big address - self.assertEqual(0xFF, ih[2**32-1]) + ih[2**32-1] = 1 + self.assertEqual(0xFF, ih[2**32-2]) # wrong addr type/value for indexing operations def getitem(index): return ih[index] @@ -661,6 +696,9 @@ def getitem(index): self.assertEqual({2:3, 10:4}, ih[2:].todict()) self.assertEqual({0:1, 2:3, 10:4}, ih[::2].todict()) self.assertEqual({10:4}, ih[3:11].todict()) + # https://github.com/python-intelhex/intelhex/issues/52 + self.assertEqual({}, ih[0:0].todict()) + self.assertEqual({}, ih[1:1].todict()) def test__setitem__(self): ih = IntelHex() @@ -809,6 +847,13 @@ def test_segments(self): self.assertEqual(max(sg[0]), 0x102) self.assertEqual(min(sg[1]), 0x200) self.assertEqual(max(sg[1]), 0x203) + ih[0x204] = 5 + sg = ih.segments() + self.assertEqual(len(sg), 3) + sg = ih.segments(min_gap=2) + self.assertEqual(len(sg), 2) + self.assertEqual(min(sg[1]), 0x200) + self.assertEqual(max(sg[1]), 0x205) pass class TestIntelHexLoadBin(TestIntelHexBase): @@ -964,6 +1009,50 @@ def test_putsz(self): self.ih.putsz(0x03, asbytes('hello')) self.assertEqual(asbytes('\x00\x01\x02hello\x00\x09'), self.ih.gets(0, 10)) + def test_find(self): + self.assertEqual(0, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'))) + self.assertEqual(0, self.ih.find(asbytes('\x00'))) + self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'))) + self.assertEqual(3, self.ih.find(asbytes('\x03'))) + self.assertEqual(7, self.ih.find(asbytes('\x07\x08\x09'))) + self.assertEqual(7, self.ih.find(asbytes('\x07'))) + self.assertEqual(-1, self.ih.find(asbytes('\x0a'))) + self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'))) + self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'))) + + def test_find_start(self): + self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), start=3)) + self.assertEqual(-1, self.ih.find(asbytes('\x00'), start=3)) + self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'), start=3)) + self.assertEqual(3, self.ih.find(asbytes('\x03'), start=3)) + self.assertEqual(7, self.ih.find(asbytes('\x07\x08\x09'), start=3)) + self.assertEqual(7, self.ih.find(asbytes('\x07'), start=3)) + self.assertEqual(-1, self.ih.find(asbytes('\x0a'), start=3)) + self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), start=3)) + self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), start=3)) + + def test_find_end(self): + self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), end=4)) + self.assertEqual(0, self.ih.find(asbytes('\x00'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x03\x04\x05\x06'), end=4)) + self.assertEqual(3, self.ih.find(asbytes('\x03'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x07\x08\x09'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x07'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x0a'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), end=4)) + self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), end=4)) + + def test_find_start_end(self): + self.assertEqual(-1, self.ih.find(asbytes('\x00\x01\x02\x03\x04\x05\x06'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x00'), start=3, end=7)) + self.assertEqual(3, self.ih.find(asbytes('\x03\x04\x05\x06'), start=3, end=7)) + self.assertEqual(3, self.ih.find(asbytes('\x03'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x07\x08\x09'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x07'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x0a'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x02\x01'), start=3, end=7)) + self.assertEqual(-1, self.ih.find(asbytes('\x08\x07'), start=3, end=7)) + class TestIntelHexDump(TestIntelHexBase): @@ -1645,19 +1734,19 @@ def test_setup_version(self): self.versionChecker('%s setup.py --version') def test_sripts_bin2hex_version(self): - self.versionChecker('%s scripts/bin2hex.py --version') + self.versionChecker('%s intelhex/scripts/bin2hex.py --version') def test_sripts_hex2bin_version(self): - self.versionChecker('%s scripts/hex2bin.py --version') + self.versionChecker('%s intelhex/scripts/hex2bin.py --version') def test_sripts_hex2dump_version(self): - self.versionChecker('%s scripts/hex2dump.py --version') + self.versionChecker('%s intelhex/scripts/hex2dump.py --version') def test_sripts_hexdiff_version(self): - self.versionChecker('%s scripts/hexdiff.py --version') + self.versionChecker('%s intelhex/scripts/hexdiff.py --version') def test_sripts_hexmerge_version(self): - self.versionChecker('%s scripts/hexmerge.py --version') + self.versionChecker('%s intelhex/scripts/hexmerge.py --version') class TestWriteHexFileByteCount(unittest.TestCase): diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..76a844a --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,6 @@ +# requirements for development and release process +twine # PyPi interaction, long description check in setup.py (twine check dist/intelhex-*.whl) +rst2html # documentation generation +sphinx # documentation generation +docutils # sphinx and rest2html dependency +pdoc # API documentation diff --git a/setup.cfg b/setup.cfg index 9ed9ac5..14536a5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,2 @@ -[wheel] +[bdist_wheel] universal = 1 diff --git a/setup.py b/setup.py index cb69dcf..54d4a6d 100755 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Copyright (c) 2008-2018, Alexander Belchenko # All rights reserved. @@ -35,41 +35,57 @@ """Setup script for IntelHex.""" -import sys, glob -from distutils.core import Command, setup +import os, sys, glob +try: + from setuptools import setup +except ImportError: + from distutils.core import setup +from distutils.core import Command import intelhex, intelhex.__version__ +LONG_DESCRIPTION = open('README.rst', 'r').read() + +SCRIPT_MODULES = [os.path.splitext(x)[0] for x in glob.glob('intelhex/scripts/*')] METADATA = dict( name='intelhex', version=intelhex.__version__.version_str, - scripts=glob.glob('scripts/*'), - packages=['intelhex'], + # Include the scripts as a subpackage + packages=['intelhex', 'intelhex.scripts'], + + # For every script file, add an entrypoint + entry_points={ + "console_scripts": [ + "{name}.py = intelhex.scripts.{name}:main".format(name=os.path.basename(x)) + for x in SCRIPT_MODULES + ] + }, author='Alexander Belchenko', author_email='alexander.belchenko@gmail.com', - url='https://pypi.python.org/pypi/IntelHex', - - description='Python Intel Hex library', - long_description='Python Intel Hex library', + maintainer='Bert van Hall', + maintainer_email='bert.vanhall@gmx.de', + url='https://github.com/python-intelhex/intelhex', + description='Python library for Intel HEX files manipulations', + long_description=LONG_DESCRIPTION, keywords='Intel HEX hex2bin HEX8', license='BSD', classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', - 'Classifier: Development Status :: 5 - Production/Stable', - 'Classifier: Environment :: Console', - 'Classifier: Intended Audience :: Developers', - 'Classifier: Intended Audience :: Telecommunications Industry', - 'Classifier: License :: OSI Approved :: BSD License', - 'Classifier: Operating System :: OS Independent', - 'Classifier: Programming Language :: Python', - 'Classifier: Topic :: Scientific/Engineering', - 'Classifier: Topic :: Software Development :: Embedded Systems', - 'Classifier: Topic :: Utilities', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], ) @@ -129,7 +145,7 @@ def main(): metadata = METADATA.copy() metadata['cmdclass'] = { 'test': test, - #'bench': bench, # bench is out of date + 'bench': bench, # bench is out of date } return setup(**metadata) diff --git a/test-all-python.log b/test-all-python.log deleted file mode 100644 index b1f4664..0000000 --- a/test-all-python.log +++ /dev/null @@ -1,34 +0,0 @@ -test-all-python.py started: Tue Apr 7 16:28:05 2015 - -Check presence of python 2.4 ... Python 2.4.4 - Running tests against 2.4 ... OK -Check presence of python 2.5 ... Python 2.5.4 - Running tests against 2.5 ... OK -Check presence of python 2.6-32bit ... Python 2.6.6 - Running tests against 2.6-32bit ... OK -Check presence of python 2.6-64bit ... Python 2.6.6 - Running tests against 2.6-64bit ... OK -Check presence of python 2.7-32bit ... Python 2.7.9 - Running tests against 2.7-32bit ... OK -Check presence of python 2.7-64bit ... Python 2.7.9 - Running tests against 2.7-64bit ... OK -Check presence of python 3.2-32bit ... Python 3.2.5 - Running tests against 3.2-32bit ... OK -Check presence of python 3.2-64bit ... Python 3.2.5 - Running tests against 3.2-64bit ... OK -Check presence of python 3.3-32bit ... Python 3.3.5 - Running tests against 3.3-32bit ... OK -Check presence of python 3.3-64bit ... Python 3.3.5 - Running tests against 3.3-64bit ... OK -Check presence of python 3.4-32bit ... Python 3.4.3 - Running tests against 3.4-32bit ... OK -Check presence of python 3.4-64bit ... Python 3.4.3 - Running tests against 3.4-64bit ... OK -Check presence of python 3.5-32bit ... Python 3.5.0a3 - Running tests against 3.5-32bit ... OK -Check presence of python 3.5-64bit ... Python 3.5.0a3 - Running tests against 3.5-64bit ... OK - -Check presence of python pypy ... Python 2.7.9 (9c4588d731b7, Mar 23 2015, 20:00:36) -[PyPy 2.5.1 with MSC v.1500 32 bit] - Running tests against pypy ... OK diff --git a/test-all-python.py b/test-all-python.py deleted file mode 100755 index bc985a4..0000000 --- a/test-all-python.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/python - -# Copyright (c) 2014-2018 Alexander Belchenko -# All rights reserved. -# -# Redistribution and use in source and binary forms, -# with or without modification, are permitted provided -# that the following conditions are met: -# -# * Redistributions of source code must retain -# the above copyright notice, this list of conditions -# and the following disclaimer. -# * Redistributions in binary form must reproduce -# the above copyright notice, this list of conditions -# and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the author nor the names -# of its contributors may be used to endorse -# or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, -# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -""" Ad-hoc test runner against multiple python versions. """ - -import subprocess -import sys -import time - - -# TODO: extract this as some sort of config file -if sys.platform == 'win32': - # Get from Windows registry - # HKEY_LOCAL_MACHINE\SOFTWARE\Python - # PythonCore - # 2.6 - # InstallPath - # 2.7 - # 3.3 - # 3.4 - # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\3.3\InstallPath - PYTHONS = ( - # display name, executable [full] path - #('2.3', 'C:\Python23\python'), # 2.3 is not supported - ('2.4', r'C:\Python\2.4\python'), - ('2.5', r'C:\Python\2.5\python'), - - ('2.6-32bit', r'C:\Python\2.6-32\python'), - ('2.6-64bit', r'C:\Python\2.6-64\python'), - - ('2.7-32bit', r'C:\Python\2.7-32\python'), - ('2.7-64bit', r'C:\Python\2.7-64\python'), - - ('3.2-32bit', r'C:\Python\3.2-32\python'), - ('3.2-64bit', r'C:\Python\3.2-64\python'), - - ('3.3-32bit', r'C:\Python\3.3-32\python'), - ('3.3-64bit', r'C:\Python\3.3-64\python'), - - ('3.4-32bit', r'C:\Python\3.4-32\python'), - ('3.4-64bit', r'C:\Python\3.4-64\python'), - - ('3.5-32bit', r'C:\Python\3.5-32\python'), - ('3.5-64bit', r'C:\Python\3.5-64\python'), - - ('pypy', r'C:\Python\pypy-2.5.1-win32\pypy'), - ) -else: - PYTHONS = ( - # display name, executable [full] path - ('2.4', 'python2.4'), - ('2.5', 'python2.5'), - ('2.6', 'python2.6'), - ('2.7', 'python2.7'), - ('3.3-32bit', 'python3.3-32'), - ('3.3-64bit', 'python3.3-64'), - ) - - -def main(): - retcode = 0 - not_found = [] - failed = [] - print('%s started: %s\n' % (__file__, time.asctime())) - for display_name, executable in PYTHONS: - if checkPythonExists(display_name, executable): - if not runTestWithPython(display_name, executable): - retcode = 1 - failed.append(display_name) - else: - not_found.append(display_name) - if failed or not_found: - print('\n' + '-'*20) - if failed: - print('Tests failed with pythons: %s' % (', '.join(failed))) - if not_found: - print('Not found python versions: %s' % (', '.join(not_found))) - return retcode - -def checkPythonExists(display_name, executable): - """ Run `python -V` and check that it runs OK. """ - sys.stdout.write('Check presence of python %s ... ' % display_name) - cmd = '%s -V' % executable - try: - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - except: - exc = sys.exc_info()[1] # current exception - sys.stdout.write('ERROR\n Exception: %s\n' % str(exc)) - return False - stdout, stderr = p.communicate() - retcode = p.poll() - output = '' - if stdout: - output = stdout.decode('ascii', 'replace') - elif stderr: - output = stderr.decode('ascii', 'replace') - output = output.replace('\r', '') - if not output.endswith('\n'): - output = output + '\n' - output = output.replace(u'\ufffd', u'?') - sys.stdout.write(output) - return retcode == 0 - -def runTestWithPython(display_name, executable): - """ Runs `$(PYTHON) setup.py test -q` """ - cmd = '%s setup.py test -q' % executable - sys.stdout.write(' Running tests against %s ... ' % display_name) - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - stdout, stderr = p.communicate() - retcode = p.poll() - if retcode == 0: - sys.stdout.write('OK\n') - return True - else: - sys.stdout.write('FAILED\n') - sys.stdout.write(stdout.decode('ascii', 'ignore')) - sys.stdout.write(stderr.decode('ascii', 'ignore')) - return False - -if __name__ == '__main__': - sys.exit(main()) diff --git a/test-all-python.sh b/test-all-python.sh new file mode 100755 index 0000000..ea49dc6 --- /dev/null +++ b/test-all-python.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +TARGETS="3.8 3.9 3.10 3.11 3.12 3.13" +DIR=$(dirname "$(readlink -f "$0")") + +_pyenv=$(command -v pyenv) +[ -z "$_pyenv" ] && { echo "pyenv is missing on your system."; exit 1; } + +_tox=$(command -v tox) +[ -z "$_tox" ] && { echo "tox is missing on your system."; exit 1; } + +cd "$DIR" + +echo "Installing (if needed) Python versions: $TARGETS" +for version in $TARGETS ; do + $_pyenv install --skip-existing $version +done + +echo "Enabling Python versions from pyenv..." +$_pyenv local $TARGETS + +echo "Running tox..." +$_tox + diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..c2b7b72 --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ +# tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py38, py39, py310, py311, py312, py313 +skip_missing_interpreters = True +tox_pyenv_fallback = False + +[testenv] +deps = + setuptools >= 72 + packaging >= 21 + +commands = + python setup.py test