Merging upstream version 2.4.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
5539cbc19b
commit
5f801f685c
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
|
||||
.idea/
|
||||
.cache/
|
||||
.vscode/
|
1
AUTHORS
1
AUTHORS
|
@ -25,6 +25,7 @@ This project receives help from these awesome contributors:
|
|||
- Mel Dafert
|
||||
- Andrii Kohut
|
||||
- Roland Walker
|
||||
- Doug Harris
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## Version 2.4.0
|
||||
|
||||
(released on 2025-03-10)
|
||||
|
||||
- Added format_timestamps preprocessor for per-column date/time formatting.
|
||||
|
||||
## Version 2.3.1
|
||||
|
||||
- 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."""
|
||||
|
||||
import string
|
||||
from datetime import datetime
|
||||
|
||||
from cli_helpers import utils
|
||||
from cli_helpers.compat import text_type, int_types, float_types, HAS_PYGMENTS, Token
|
||||
|
@ -125,9 +126,11 @@ def escape_newlines(data, headers, **_):
|
|||
return (
|
||||
(
|
||||
[
|
||||
v.replace("\r", r"\r").replace("\n", r"\n")
|
||||
if isinstance(v, text_type)
|
||||
else v
|
||||
(
|
||||
v.replace("\r", r"\r").replace("\n", r"\n")
|
||||
if isinstance(v, text_type)
|
||||
else v
|
||||
)
|
||||
for v in row
|
||||
]
|
||||
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
|
||||
)
|
||||
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,
|
||||
style_output,
|
||||
format_numbers,
|
||||
format_timestamps,
|
||||
)
|
||||
|
||||
if HAS_PYGMENTS:
|
||||
|
@ -348,3 +349,25 @@ def test_enforce_iterable():
|
|||
assert False, "{} doesn't return iterable".format(name)
|
||||
if isinstance(preprocessed[1], types.GeneratorType):
|
||||
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