Skip to content

Commit

Permalink
first dev release
Browse files Browse the repository at this point in the history
  • Loading branch information
AAriam committed Aug 17, 2024
1 parent 6c25206 commit 2d6f87a
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 39 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/_pkg_publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

name: 'Package - Build'
run-name: 'Package: Build from ${{github.ref}}'


on:
workflow_dispatch:
push:
paths:
- 'pyproject.toml'

jobs:

publish-pypi:
runs-on: ubuntu-latest
permissions:
id-token: write
environment:
name: PyPI
steps:
- name: 'Checkout repository from ${{github.ref}}'
uses: actions/checkout@v4

- name: 'Build sdist'
run: |
pipx run build --sdist --wheel --outdir dist/
- name: 'Upload package'
uses: pypa/gh-action-pypi-publish@release/v1
# https://github.com/marketplace/actions/pypi-publish
with:
packages-dir: dist
verify-metadata: false
verbose: true
print-hash: true
skip-existing: false

publish-testpypi:
runs-on: ubuntu-latest
permissions:
id-token: write
environment:
name: TestPyPI
steps:
- name: 'Checkout repository from ${{github.ref}}'
uses: actions/checkout@v4

- name: 'Build sdist'
run: |
pipx run build --sdist --wheel --outdir dist/
- name: 'Upload package'
uses: pypa/gh-action-pypi-publish@release/v1
# https://github.com/marketplace/actions/pypi-publish
with:
packages-dir: dist
repository-url: https://test.pypi.org/legacy/
verify-metadata: false
verbose: true
print-hash: true
skip-existing: false
65 changes: 26 additions & 39 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Ref: https://github.com/github/gitignore

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand All @@ -24,7 +26,6 @@ share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
Expand All @@ -46,9 +47,8 @@ htmlcov/
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
.pytest_cache
cover/

# Translations
Expand All @@ -69,7 +69,8 @@ instance/
.scrapy

# Sphinx documentation
docs/_build/
docs/website/_build/
docs/website/source/api/_autosummary

# PyBuilder
.pybuilder/
Expand All @@ -83,36 +84,7 @@ profile_default/
ipython_config.py

# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
.python-version

# Celery stuff
celerybeat-schedule
Expand Down Expand Up @@ -146,6 +118,7 @@ venv.bak/
dmypy.json

# Pyre type checker
# There are reports this comes from LLVM profiling, but also Xcode 9.
.pyre/

# pytype static type analyzer
Expand All @@ -154,9 +127,23 @@ dmypy.json
# Cython debug symbols
cython_debug/

# profraw files from LLVM? Unclear exactly what triggers this
# There are reports this comes from LLVM profiling, but also Xcode 9.
*profraw

# In-tree generated files
*/_version.py

# VSCode
.vscode/

# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/

# MacOS system files
.DS_Store

# PyPackIT
data/_local_reports/
data/_cache/
!/dev/build/
22 changes: 22 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

[build-system]
requires = ["setuptools>=61.0", "versioningit"]
build-backend = "setuptools.build_meta"


# ----------------------------------------- setuptools -------------------------------------------
[tool.setuptools]
include-package-data = true
zip-safe = false

[tool.setuptools.packages.find]
where = ["src"]
namespaces = true


# ----------------------------------------- Project Metadata -------------------------------------
#
[project]
version = "0.0.0.dev1"
name = "ANSI-SGR"
requires-python = ">=3.10"
Empty file added src/ansi_sgr/__init__.py
Empty file.
134 changes: 134 additions & 0 deletions src/ansi_sgr/sgr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""
References
----------
- https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
"""

from typing import Optional
import re


_TEXT_STYLE = {
'normal': '0',
'bold': '1',
'faint': '2',
'italic': '3',
'underline': '4',
'blink': '5',
'blink_fast': '6',
'reverse': '7',
'conceal': '8',
'strike': '9',
}
_COLOR = {
'black': 30,
'red': 31,
'green': 32,
'yellow': 33,
'blue': 34,
'magenta': 35,
'cyan': 36,
'white': 37,
'b_black': 90,
'b_red': 91,
'b_green': 92,
'b_yellow': 93,
'b_blue': 94,
'b_magenta': 95,
'b_cyan': 96,
'b_white': 97,
}
_TEMPLATE = "\033[{}m"
RESET = _TEMPLATE.format(0)


def style(
text_styles: int | str | list[int | str] = None,
text_color: int | str | tuple = None,
background_color: int | str | tuple = None
):

def add_color(color: int | str | tuple, bg: bool = False):
int_range = (
list(range(40, 48)) + list(range(100, 108)) if bg else
list(range(30, 38)) + list(range(90, 98))
)
int_offset = 10 if bg else 0
rgb_code = 48 if bg else 38
if isinstance(color, int):
if color not in int_range:
raise ValueError(f"Invalid color code: {color}")
return f"{color};"
if isinstance(color, str):
if color not in _COLOR:
raise ValueError(f"Invalid color name: {color}")
return f"{_COLOR[color] + int_offset};"
if isinstance(color, tuple):
if len(color) != 3:
raise ValueError(f"Invalid color tuple: {color}")
if not all(isinstance(c, int) for c in color):
raise ValueError(f"Invalid color tuple: {color}")
if not all(c in range(256) for c in color):
raise ValueError(f"Invalid color tuple: {color}")
return f"{rgb_code};2;{';'.join([str(c) for c in color])};"
raise TypeError(f"Invalid color type: {type(color)}")

style_str = ""
if text_styles:
if isinstance(text_styles, (str, int)):
text_styles = [text_styles]
if not isinstance(text_styles, list):
raise TypeError("styles must be a string, an integer, or a list of strings or integers")
for text_style in text_styles:
if isinstance(text_style, int):
if text_style not in range(10):
raise ValueError(f"Invalid style code: {text_style}")
text_style_code = str(text_style)
elif isinstance(text_style, str):
if text_style not in _TEXT_STYLE:
raise ValueError(f"Invalid style name: {text_style}")
text_style_code = _TEXT_STYLE[text_style]
else:
raise TypeError(f"Invalid style type: {type(text_style)}")
style_str += f"{text_style_code};"
if text_color:
style_str += add_color(text_color, bg=False)
if background_color:
style_str += add_color(background_color, bg=True)
if not style_str:
return RESET
return _TEMPLATE.format(style_str.removesuffix(";"))


def format(text, control_sequence: str):
return f"{control_sequence}{text}{RESET}"


def remove_format(text: str):
"""
Remove ANSI escape sequences from a string.
Parameters
----------
text
Notes
-----
ANSI escape codes start with the \033 (\x1b) escape character,
followed by '[', then zero or more numbers separated by ';', and ending with a letter.
Regex Details:
- [0-?]*: This part matches zero or more characters in the range between 0 and ?.
This covers all the numbers and semicolons that might be present in the escape sequence.
For example, in the code \x1b[31;42m, 31 and 42 are matched by this part.
- [ -/]*: This is a sequence of characters that might appear in some of the ANSI sequences.
It's more of a catch-all for certain sequences and may not be strictly necessary for many common sequences.
But it ensures we catch even those rare ANSI codes.
- [@-~]: Finally, ANSI escape sequences end with a character from @ to ~:
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
[ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z {.
This part matches that ending character. This character typically indicates what action
should be taken (e.g., change color, move cursor, clear screen, etc.).
"""
return re.compile(r'\x1b\[[0-?]*[ -/]*[@-~]').sub('', text)

0 comments on commit 2d6f87a

Please sign in to comment.