Skip to content

Commit 397f6bb

Browse files
committed
0.0.1
0 parents  commit 397f6bb

File tree

10 files changed

+204
-0
lines changed

10 files changed

+204
-0
lines changed

.github/workflows/pypi.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: PyPI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'version.py'
9+
10+
jobs:
11+
pypi:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v3
15+
- name: Set up Python
16+
uses: actions/setup-python@v1
17+
with:
18+
python-version: '3.x'
19+
- name: Install dependencies
20+
run: |
21+
python3 -m pip install --upgrade pip
22+
pip install setuptools wheel twine
23+
- name: Build and publish
24+
env:
25+
TWINE_USERNAME: __token__
26+
TWINE_PASSWORD: ${{ secrets.pypi_token }}
27+
run: |
28+
python setup.py sdist bdist_wheel
29+
twine upload dist/* --skip-existing
30+
- name: Tag
31+
run: |
32+
TAG=$(python -c 'from version import __version__; print(__version__)' | sed 's/^/v/')
33+
git config --global user.name 'github-actions'
34+
git config --global user.email '[email protected]'
35+
git tag -a $TAG -m"Release $TAG"
36+
git push origin $TAG --force

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.mypy_cache
2+
pythoned.egg-info
3+
**/__pycache__
4+
.venv
5+
build

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# `pythoned`
2+
3+
### *PYTHON EDitor: edit lines via Python expressions*
4+
5+
> For those knowing Python and tired of forgetting `sed`/`awk`/`grep`/`tr` syntaxes.
6+
7+
## install
8+
```bash
9+
pip install pythoned
10+
```
11+
(it installs `pythoned` in your PATH)
12+
13+
## edit
14+
The provided Python expression must be an `str`. It manipulates the line stored in the variable `_: str`:
15+
16+
```bash
17+
# get last char of each line
18+
echo -e 'f00\nbar\nf00bar' | pythoned '_[-1]'
19+
```
20+
output:
21+
```
22+
0
23+
r
24+
r
25+
```
26+
27+
## filter
28+
If the expression is a `bool` instead of an `str`, then the lines will be filtered according to it:
29+
```bash
30+
# keep only lines containing a zero
31+
echo -e 'f00\nbar\nf00bar' | pythoned '"0" in _'
32+
```
33+
output:
34+
```
35+
f00
36+
f00bar
37+
```
38+
39+
### modules auto-import
40+
41+
Modules are auto-imported, example with `re`:
42+
```bash
43+
# replace digits by Xs
44+
echo -e 'f00\nbar\nf00bar' | pythoned 're.sub(r"\d", "X", _)'
45+
```
46+
output:
47+
```
48+
fXX
49+
bar
50+
fXXbar
51+
```

pythoned/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from pythoned.edit import edit

pythoned/__main__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import argparse
2+
import sys
3+
4+
from pythoned.edit import edit
5+
6+
7+
def main() -> int:
8+
arg_parser = argparse.ArgumentParser()
9+
arg_parser.add_argument("expression")
10+
11+
args = arg_parser.parse_args()
12+
expression: str = args.expression
13+
for output_line in edit(sys.stdin, expression):
14+
print(output_line, end="")
15+
return 0
16+
17+
18+
if __name__ == "__main__":
19+
main()

pythoned/edit.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import importlib
2+
import os
3+
import re
4+
from types import ModuleType
5+
from typing import Dict, Iterator
6+
7+
8+
TYPE_ERROR_MSG = "The provided expression must be an str (editing) or a bool (filtering), but got {}."
9+
10+
11+
def edit(lines: Iterator[str], expression) -> Iterator[str]:
12+
modules: Dict[str, ModuleType] = {}
13+
for line in lines:
14+
linesep = ""
15+
if line.endswith(os.linesep):
16+
linesep, line = os.linesep, line[: -len(os.linesep)]
17+
globals = {"_": line, **modules}
18+
try:
19+
value = eval(expression, globals)
20+
except NameError as name_error:
21+
match = re.match(r"name '([A-Za-z]+)'.*", str(name_error))
22+
if match:
23+
module = match.group(1)
24+
else:
25+
raise name_error
26+
try:
27+
modules[module] = importlib.import_module(module)
28+
globals = {"_": line, **modules}
29+
except:
30+
raise name_error
31+
value = eval(expression, globals)
32+
if isinstance(value, str):
33+
yield value + linesep
34+
elif isinstance(value, bool):
35+
if value:
36+
yield line + linesep
37+
else:
38+
raise TypeError(TYPE_ERROR_MSG.format(type(value)))

setup.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from setuptools import find_packages, setup # type: ignore
2+
from version import __version__
3+
4+
setup(
5+
name="pythoned",
6+
version=__version__,
7+
packages=find_packages(),
8+
url="http://github.com/ebonnal/pythoned",
9+
license="Apache 2.",
10+
author="ebonnal",
11+
author_email="[email protected]",
12+
description="PYTHON EDitor: edit lines via Python expressions",
13+
long_description=open("README.md").read(),
14+
long_description_content_type="text/markdown",
15+
entry_points={
16+
"console_scripts": [
17+
"pythoned=pythoned.__main__:main",
18+
],
19+
},
20+
)

tests/__init__.py

Whitespace-only changes.

tests/test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Iterator
2+
import unittest
3+
4+
from pythoned.__main__ import edit
5+
6+
7+
def lines() -> Iterator[str]:
8+
return iter(["f00\n", "bar\n", "f00bar"])
9+
10+
11+
class TestStream(unittest.TestCase):
12+
def test_edit(self) -> None:
13+
self.assertEqual(
14+
list(edit(lines(), "_[-1]")),
15+
["0\n", "r\n", "r"],
16+
msg="str expression must edit the lines",
17+
)
18+
self.assertEqual(
19+
list(edit(lines(), 're.sub(r"\d", "X", _)')),
20+
["fXX\n", "bar\n", "fXXbar"],
21+
msg="str expression must edit the lines and re should be supported",
22+
)
23+
self.assertEqual(
24+
list(edit(lines(), '"0" in _')),
25+
["f00\n", "f00bar"],
26+
msg="bool expression must filter the lines",
27+
)
28+
self.assertEqual(
29+
list(edit(lines(), "str(int(math.pow(10, len(_))))")),
30+
["1000\n", "1000\n", "1000000"],
31+
msg="modules should be auto-imported",
32+
)

version.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# to show the CHANGELOG: git log -- version.py
2+
__version__ = "0.0.1"

0 commit comments

Comments
 (0)