Adding upstream version 2.4.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
c905e7ab8c
commit
8f9294000b
7 changed files with 120 additions and 4 deletions
41
.github/workflows/codeql.yml
vendored
Normal file
41
.github/workflows/codeql.yml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
schedule:
|
||||||
|
- cron: "36 4 * * 1"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ python ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
queries: +security-and-quality
|
||||||
|
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v2
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
||||||
|
with:
|
||||||
|
category: "/language:${{ matrix.language }}"
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,3 +11,4 @@ __pycache__
|
||||||
/cli_helpers_dev
|
/cli_helpers_dev
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
|
.vscode/
|
1
AUTHORS
1
AUTHORS
|
@ -25,6 +25,7 @@ This project receives help from these awesome contributors:
|
||||||
- Mel Dafert
|
- Mel Dafert
|
||||||
- Andrii Kohut
|
- Andrii Kohut
|
||||||
- Roland Walker
|
- Roland Walker
|
||||||
|
- Doug Harris
|
||||||
|
|
||||||
Thanks
|
Thanks
|
||||||
------
|
------
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 2.4.0
|
||||||
|
|
||||||
|
(released on 2025-03-10)
|
||||||
|
|
||||||
|
- Added format_timestamps preprocessor for per-column date/time formatting.
|
||||||
|
|
||||||
## Version 2.3.1
|
## Version 2.3.1
|
||||||
|
|
||||||
- Don't escape newlines in `ascii` tables, and add `ascii_escaped` table format.
|
- Don't escape newlines in `ascii` tables, and add `ascii_escaped` table format.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "2.3.1"
|
__version__ = "2.4.0"
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
"""These preprocessor functions are used to process data prior to output."""
|
"""These preprocessor functions are used to process data prior to output."""
|
||||||
|
|
||||||
import string
|
import string
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from cli_helpers import utils
|
from cli_helpers import utils
|
||||||
from cli_helpers.compat import text_type, int_types, float_types, HAS_PYGMENTS, Token
|
from cli_helpers.compat import text_type, int_types, float_types, HAS_PYGMENTS, Token
|
||||||
|
@ -125,9 +126,11 @@ def escape_newlines(data, headers, **_):
|
||||||
return (
|
return (
|
||||||
(
|
(
|
||||||
[
|
[
|
||||||
|
(
|
||||||
v.replace("\r", r"\r").replace("\n", r"\n")
|
v.replace("\r", r"\r").replace("\n", r"\n")
|
||||||
if isinstance(v, text_type)
|
if isinstance(v, text_type)
|
||||||
else v
|
else v
|
||||||
|
)
|
||||||
for v in row
|
for v in row
|
||||||
]
|
]
|
||||||
for row in data
|
for row in data
|
||||||
|
@ -351,3 +354,44 @@ def format_numbers(
|
||||||
[_format_number(v, column_types[i]) for i, v in enumerate(row)] for row in data
|
[_format_number(v, column_types[i]) for i, v in enumerate(row)] for row in data
|
||||||
)
|
)
|
||||||
return data, headers
|
return data, headers
|
||||||
|
|
||||||
|
|
||||||
|
def format_timestamps(data, headers, column_date_formats=None, **_):
|
||||||
|
"""Format timestamps according to user preference.
|
||||||
|
|
||||||
|
This allows for per-column formatting for date, time, or datetime like data.
|
||||||
|
|
||||||
|
Add a `column_date_formats` section to your config file with separate lines for each column
|
||||||
|
that you'd like to specify a format using `name=format`. Use standard Python strftime
|
||||||
|
formatting strings
|
||||||
|
|
||||||
|
Example: `signup_date = "%Y-%m-%d"`
|
||||||
|
|
||||||
|
:param iterable data: An :term:`iterable` (e.g. list) of rows.
|
||||||
|
:param iterable headers: The column headers.
|
||||||
|
:param str column_date_format: The format strings to use for specific columns.
|
||||||
|
:return: The processed data and headers.
|
||||||
|
:rtype: tuple
|
||||||
|
|
||||||
|
"""
|
||||||
|
if column_date_formats is None:
|
||||||
|
return iter(data), headers
|
||||||
|
|
||||||
|
def _format_timestamp(value, name, column_date_formats):
|
||||||
|
if name not in column_date_formats:
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
dt = datetime.fromisoformat(value)
|
||||||
|
return dt.strftime(column_date_formats[name])
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
# not a date
|
||||||
|
return value
|
||||||
|
|
||||||
|
data = (
|
||||||
|
[
|
||||||
|
_format_timestamp(v, headers[i], column_date_formats)
|
||||||
|
for i, v in enumerate(row)
|
||||||
|
]
|
||||||
|
for row in data
|
||||||
|
)
|
||||||
|
return data, headers
|
||||||
|
|
|
@ -16,6 +16,7 @@ from cli_helpers.tabular_output.preprocessors import (
|
||||||
override_tab_value,
|
override_tab_value,
|
||||||
style_output,
|
style_output,
|
||||||
format_numbers,
|
format_numbers,
|
||||||
|
format_timestamps,
|
||||||
)
|
)
|
||||||
|
|
||||||
if HAS_PYGMENTS:
|
if HAS_PYGMENTS:
|
||||||
|
@ -348,3 +349,25 @@ def test_enforce_iterable():
|
||||||
assert False, "{} doesn't return iterable".format(name)
|
assert False, "{} doesn't return iterable".format(name)
|
||||||
if isinstance(preprocessed[1], types.GeneratorType):
|
if isinstance(preprocessed[1], types.GeneratorType):
|
||||||
assert False, "{} returns headers as iterator".format(name)
|
assert False, "{} returns headers as iterator".format(name)
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_timestamps():
|
||||||
|
data = (
|
||||||
|
("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"),
|
||||||
|
("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"),
|
||||||
|
("name3", None, "not-actually-timestamp", "2025-02-13T02:32:22"),
|
||||||
|
)
|
||||||
|
headers = ["name", "date_col", "datetime_col", "unchanged_col"]
|
||||||
|
column_date_formats = {
|
||||||
|
"date_col": "%Y-%m-%d",
|
||||||
|
"datetime_col": "%I:%M:%S %m/%d/%y",
|
||||||
|
}
|
||||||
|
result_data, result_headers = format_timestamps(data, headers, column_date_formats)
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
["name1", "2024-12-13", "07:32:22 12/13/24", "2024-12-13T20:32:22"],
|
||||||
|
["name2", "2025-02-13", "02:32:22 02/13/25", "2025-02-13T02:32:22"],
|
||||||
|
["name3", None, "not-actually-timestamp", "2025-02-13T02:32:22"],
|
||||||
|
]
|
||||||
|
assert expected == list(result_data)
|
||||||
|
assert headers == result_headers
|
||||||
|
|
Loading…
Add table
Reference in a new issue