
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
header-detail-footer
Advanced tools
The header_detail_footer module provides a way to parse input iterables (usually text files) that contain header rows, an unknown number of data rows (called the details), and footer rows. The number of header and footer rows must be specified when parsing begins. There can be zero or more header rows, and independently zero or more footer rows. The detail rows are all of the rows between the header and footer. If a file consists of just headers and footers, there will be zero detail rows.
The module API consists of a single function, parse(), and several exceptions.
Note that the contents of each input "row" are never inspected: they are just iterated over and returned by the parser. They are often strings, but they could be any object.
This code shows a simple usage of the parse()
function::
>>> from header_detail_footer import parse
>>> header, details, footer = parse(['header', 'row 1', 'row 2', 'footer'])
>>> header
'header'
>>> list(details)
['row 1', 'row 2']
>>> footer()
'footer'
The parse() function takes 1 required parameter, the input iterable. There are two optional parameters, header_rows and footer_rows. Both default to 1. They represent the number of header and footer rows present in the input, respectively.
parse() returns a 3-tuple: (header, details, footer). header is the header row(s), if any; details is an iterator returning each detail row; and footer is a callable returning the footer row(s), if any.
For header and footer(), they return a single row from the input if header_rows or footer_rows is 1, respectively. Otherwise, including the case of 0 rows, they contain a list::
>>> header, details, footer = parse(['row 1', 'row 2', 'footer 1', 'footer 2'],
... header_rows=0, footer_rows=2)
>>> header
[]
>>> list(details)
['row 1', 'row 2']
>>> footer()
['footer 1', 'footer 2']
The returned footer callable need never be called. If footer is ever called, details must have been exhausted, otherwise a RuntimeError is raised::
>>> header, detail, footer = parse('abc')
>>> footer()
Traceback (most recent call last):
...
RuntimeError: called footer() before details were exhausted
Two exceptions are defined at the module level: HeaderError
and
FooterError
. Note that these exceptions are raised in the parser()
method. They are never raised when iterating over the header, details,
or footer.
Raised by parser()
if the input does not contain enough rows for the header::
>>> header, details, footer = parse(['row 1'], header_rows=3) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
HeaderError: too few rows for header
Raised by parser()
if the input does contain enough rows for the
header, but not enough rows for the footer::
>>> header, details, footer = parse(['row 1', 'row 2', 'row 3'],
... header_rows=2, footer_rows=2) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
FooterError: too few rows for footer
If you have a CSV file with a header row containing column names, and also with a footer row, it's easiest if you tell header_detail_footer that there is no header row. That way, the details iterator can be passed to csv.DictReader, which will pick up the header row as usual::
>>> import csv
# here, the footer row contains a row count
>>> _, details, footer = parse(['FRUIT,COLOR',
... 'apple,red',
... 'orange,orange',
... 'rowcount:2'],
... header_rows=0) # specify 0 header rows
# pass the details to csv.DictReader. this includes what is now
# the csv header row
>>> reader = csv.DictReader(details)
# print out each row
>>> for count, row in enumerate(reader, 1):
... (count, [(key, value) for key, value in sorted(row.items())])
...
(1, [('COLOR', 'red'), ('FRUIT', 'apple')])
(2, [('COLOR', 'orange'), ('FRUIT', 'orange')])
# verify the footer count
>>> _, _, footer_count = footer().partition(':')
>>> int(footer_count) == count
True
Renamed distribution name to replace hyphens with underscores. The name is now header_detail_footer (issue #7).
Remove hack for renaming RPMs (issue #5).
Always require setuptools (issue #4).
No code changes.
Added MANIFEST.in to MANIFEST.in (issue #2).
Have bdist_rpm use the package name 'python-header-detail-footer' (issue #3).
Add documentation about CSV files.
Change protocol error from ValueError to RuntimeError. Closes Bitbucket issue #1.
Changed API to return a callable only for footer, since that's the only thing that needs to be delayed after details are exhausted.
Changed nomenclature: now refers to "rows" instead of "lines".
FAQs
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.