Arbitrarily complex single-line Python scripts
==============================================
**`pysln`** allows executing "Python"-ish scripts in the command line as parts of a pipeline.
_Why?_ Normally, the Python binary allows executing Python script specified in the command-line, but you have to write a proper Python script:
~~~~
python3 -c 'import json
print(json.dumps({0: 1}))
'
~~~~
This may make shell history unreadable, hard to edit, etc.
In addition, accessing information in a pipe, common with most command-line tools, is convoluted.
While you can easily say `some-command-generating-data | grep Foo | awk '{ print $2; }'`, doing a similar thing for data processing in Python is really hard, requiring you to open an editor, save a script file.
**`pysln`** takes this need off for **quick and dirty** command-line data processing.
Installation
------------
Install from [PyPI](http://pypi.org/project/pysln/).
The `pysln` entry point will be made available.
~~~
$ pip3 install pysln
$ pysln -h
usage: pysln [-h] [...]
~~~
Overview
--------
Use `pysln` just like you would use `sed` or `awk` in a pipe.
After the optional flags, specify the code to execute:
~~~~
$ seq 1 5 | pysln 'print(int(LINE) * 2);'
2
4
6
8
10
~~~~
_Py-SingleLine_ works by first *transcoding* the input code to real Python syntax, then *injecting* this input into a context, forming a full source code, and then running this code.
### Optional arguments
Help about individual modes is printed if no code is specified:
~~~~
$ pysln -t lines
Help for the 'lines' mode ...
~~~~
* **`-n`**: Show the result of the transformed code, but do not execute.
* **`-b`**: Show the result of the transformed and injected code, but do not execute.
* **`-t XXX`**: use _`XXX`_ mode.
### Passing command-line arguments
Command-line arguments to `pysln` can be passed to the running script with the **`-X`** optional argument.
The argument vector (list) of the invocation is available inside the script as `ARGS`.
~~~~
$ pysln -X "username" -X "$(date)" 'print(ARGS[1], ARGS[2])'
username "Sun 14 Feb 2021 14:02:33"
~~~~
Usage modes
-----------
### `bare` mode
The bare mode does not perform any pre-parsing or business logic.
This is the default mode when the standard input is a terminal, and not a pipe.
The variables `STDIN`, `STDOUT`, and `STDERR` alias `sys.stdin`, `sys.stdout`, and `sys.stderr`, respectively.
### `lines` mode
Lines mode allows handling each line of the standard input.
This is the default mode if the standard input comes from a pipe.
The values are available through the `LINE` variable.
The functions `OUT` and `ERR` print the arguments verbatim to the standard output and error respectively, without adding an additional trailing newline.
#### FizzBuzz
~~~~
$ seq 1 15 | pysln 'if int(LINE) % 15 == 0: print("Fizzbuzz"); ' \
'elif int(LINE) % 3 == 0: print("Fizz");' \
'elif int(LINE) % 5 == 0: print("Buzz");' \
'else: print(LINE); endif'
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizzbuzz
~~~~
### `csv` mode
The input stream is loaded and parsed as a [CSV](http://docs.python.org/3.6/library/csv.html) file, with each row made available as `ROW`.
The `HEADER()` function returns `True` if the first row is in `ROW`.
After the user's code is executed and the rows are transformed one by one, the resulting CSV is printed to the standard output.
~~~~
$ echo -e "Foo,Bar,Baz\n1,2,3\n4,5,6" | \
pysln -t csv 'for idx, elem in enumerate(ROW): ' \
'if not HEADER(): ROW[idx] = int(elem) * 10; endif; endfor;'
Foo,Bar,Baz
10,20,30
40,50,60
~~~~
### `json` mode
The input stream is loaded and parsed as a [JSON](http://json.org), and made available as `DATA`.
This mode is aimed at implementing JSON filters and transformers.
After the user's code executed, the JSON is formatted and printed to the standard output.
#### JSON filter
~~~~
$ echo '[{"Timezone": "Europe/London"}, {"Timezone": "America/New York"}]' | \
pysln -t json 'for rec in DATA: for k, v in dict(rec).items(): ' \
'if k == "Timezone": split = v.split("/"); ' \
'rec["Country"] = split[0]; rec["City"] = split[1]; del rec["Timezone"]; ' \
'endif; endfor; endfor;'
[{"Country": "Europe", "City": "London"}, {"Country": "America", "City": "New York"}]
~~~~
The JSON mode also offers the `PRETTY()` function, which turns out formatted JSON output:
~~~~
$ echo '[{"Timezone": "Europe/London"}, {"Timezone": "America/New York"}]' | \
pysln -t json 'for rec in DATA: for k, v in dict(rec).items(): ' \
'if k == "Timezone": split = v.split("/"); ' \
'rec["Country"] = split[0]; rec["City"] = split[1]; del rec["Timezone"]; ' \
'endif; endfor; endfor; ' \
'PRETTY();'
[
{
"City": "London",
"Country": "Europe"
},
{
"City": "New York",
"Country": "America"
}
]
~~~~
Syntax
------
The code given to `pysln` is generally the same as normal Python code, except for a few key differences:
* **Lines are terminated by `;` (semicolon)**, instead of a newline. Newlines still work, but the entire idea is to not deal with newlines.
* Due to not dealing with newlines and whitespace, the indentation-based "scoping" is also side-stepped:
* Everything that would begin a scope and require indented code is instead closed with an `end___` keyword.
* For example: `if X: print(X); endif;`, `while True: pass; endwhile;`, `def identity(a): return a; enddef`.
Everything else in-between is expected to behave as it would in Python.
Raw data
{
"_id": null,
"home_page": "http://github.com/whisperity/py-singleline",
"name": "py-singleline",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "python shell pipeline script scripting",
"author": "Whisperity",
"author_email": "whisperity-packages@protonmail.com",
"download_url": "https://files.pythonhosted.org/packages/99/21/3c3fd9b0be797d145b7e1f39dfac57814234045ea166235983586d6d48f0/py-singleline-1.0.0a2.tar.gz",
"platform": null,
"description": "Arbitrarily complex single-line Python scripts\n==============================================\n\n**`pysln`** allows executing \"Python\"-ish scripts in the command line as parts of a pipeline.\n\n_Why?_ Normally, the Python binary allows executing Python script specified in the command-line, but you have to write a proper Python script:\n\n~~~~\npython3 -c 'import json\nprint(json.dumps({0: 1}))\n'\n~~~~\n\nThis may make shell history unreadable, hard to edit, etc.\n\nIn addition, accessing information in a pipe, common with most command-line tools, is convoluted.\nWhile you can easily say `some-command-generating-data | grep Foo | awk '{ print $2; }'`, doing a similar thing for data processing in Python is really hard, requiring you to open an editor, save a script file.\n\n**`pysln`** takes this need off for **quick and dirty** command-line data processing.\n\nInstallation\n------------\n\nInstall from [PyPI](http://pypi.org/project/pysln/).\nThe `pysln` entry point will be made available.\n\n~~~\n$ pip3 install pysln\n$ pysln -h\nusage: pysln [-h] [...]\n~~~\n\nOverview\n--------\n\nUse `pysln` just like you would use `sed` or `awk` in a pipe.\nAfter the optional flags, specify the code to execute:\n\n~~~~\n$ seq 1 5 | pysln 'print(int(LINE) * 2);'\n2\n4\n6\n8\n10\n~~~~\n\n_Py-SingleLine_ works by first *transcoding* the input code to real Python syntax, then *injecting* this input into a context, forming a full source code, and then running this code.\n\n### Optional arguments\n\nHelp about individual modes is printed if no code is specified:\n\n~~~~\n$ pysln -t lines\nHelp for the 'lines' mode ...\n~~~~\n\n * **`-n`**: Show the result of the transformed code, but do not execute.\n * **`-b`**: Show the result of the transformed and injected code, but do not execute.\n * **`-t XXX`**: use _`XXX`_ mode.\n\n### Passing command-line arguments\n\nCommand-line arguments to `pysln` can be passed to the running script with the **`-X`** optional argument.\nThe argument vector (list) of the invocation is available inside the script as `ARGS`.\n\n~~~~\n$ pysln -X \"username\" -X \"$(date)\" 'print(ARGS[1], ARGS[2])'\nusername \"Sun 14 Feb 2021 14:02:33\"\n~~~~\n\n\nUsage modes\n-----------\n\n### `bare` mode\n\nThe bare mode does not perform any pre-parsing or business logic.\nThis is the default mode when the standard input is a terminal, and not a pipe.\nThe variables `STDIN`, `STDOUT`, and `STDERR` alias `sys.stdin`, `sys.stdout`, and `sys.stderr`, respectively.\n\n### `lines` mode\n\nLines mode allows handling each line of the standard input.\nThis is the default mode if the standard input comes from a pipe.\nThe values are available through the `LINE` variable.\n\nThe functions `OUT` and `ERR` print the arguments verbatim to the standard output and error respectively, without adding an additional trailing newline.\n\n#### FizzBuzz\n\n~~~~\n$ seq 1 15 | pysln 'if int(LINE) % 15 == 0: print(\"Fizzbuzz\"); ' \\\n 'elif int(LINE) % 3 == 0: print(\"Fizz\");' \\\n 'elif int(LINE) % 5 == 0: print(\"Buzz\");' \\\n 'else: print(LINE); endif'\n1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzbuzz\n~~~~\n\n### `csv` mode\n\nThe input stream is loaded and parsed as a [CSV](http://docs.python.org/3.6/library/csv.html) file, with each row made available as `ROW`.\nThe `HEADER()` function returns `True` if the first row is in `ROW`.\nAfter the user's code is executed and the rows are transformed one by one, the resulting CSV is printed to the standard output.\n\n~~~~\n$ echo -e \"Foo,Bar,Baz\\n1,2,3\\n4,5,6\" | \\\n pysln -t csv 'for idx, elem in enumerate(ROW): ' \\\n 'if not HEADER(): ROW[idx] = int(elem) * 10; endif; endfor;'\nFoo,Bar,Baz\n10,20,30\n40,50,60\n~~~~\n\n### `json` mode\n\nThe input stream is loaded and parsed as a [JSON](http://json.org), and made available as `DATA`.\nThis mode is aimed at implementing JSON filters and transformers.\nAfter the user's code executed, the JSON is formatted and printed to the standard output.\n\n#### JSON filter\n\n~~~~\n$ echo '[{\"Timezone\": \"Europe/London\"}, {\"Timezone\": \"America/New York\"}]' | \\\n pysln -t json 'for rec in DATA: for k, v in dict(rec).items(): ' \\\n 'if k == \"Timezone\": split = v.split(\"/\"); ' \\\n 'rec[\"Country\"] = split[0]; rec[\"City\"] = split[1]; del rec[\"Timezone\"]; ' \\\n 'endif; endfor; endfor;'\n[{\"Country\": \"Europe\", \"City\": \"London\"}, {\"Country\": \"America\", \"City\": \"New York\"}]\n~~~~\n\nThe JSON mode also offers the `PRETTY()` function, which turns out formatted JSON output:\n\n~~~~\n$ echo '[{\"Timezone\": \"Europe/London\"}, {\"Timezone\": \"America/New York\"}]' | \\\n pysln -t json 'for rec in DATA: for k, v in dict(rec).items(): ' \\\n 'if k == \"Timezone\": split = v.split(\"/\"); ' \\\n 'rec[\"Country\"] = split[0]; rec[\"City\"] = split[1]; del rec[\"Timezone\"]; ' \\\n 'endif; endfor; endfor; ' \\\n 'PRETTY();'\n[\n {\n \"City\": \"London\",\n \"Country\": \"Europe\"\n },\n {\n \"City\": \"New York\",\n \"Country\": \"America\"\n }\n]\n~~~~\n\n\nSyntax\n------\n\nThe code given to `pysln` is generally the same as normal Python code, except for a few key differences:\n\n * **Lines are terminated by `;` (semicolon)**, instead of a newline. Newlines still work, but the entire idea is to not deal with newlines.\n * Due to not dealing with newlines and whitespace, the indentation-based \"scoping\" is also side-stepped:\n * Everything that would begin a scope and require indented code is instead closed with an `end___` keyword.\n * For example: `if X: print(X); endif;`, `while True: pass; endwhile;`, `def identity(a): return a; enddef`.\n\nEverything else in-between is expected to behave as it would in Python.\n",
"bugtrack_url": null,
"license": "GPLv3+",
"summary": "Arbitrarily complex single-line Python scripts",
"version": "1.0.0a2",
"project_urls": {
"Homepage": "http://github.com/whisperity/py-singleline"
},
"split_keywords": [
"python",
"shell",
"pipeline",
"script",
"scripting"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1a5fead373ff97cadd1ea11e8aef92ccadca33be68993e2abbb2fc9ec4394ec3",
"md5": "39d5c381800aa225b3db381f11042b90",
"sha256": "025403648edaf2df94b9a20dcb9736d38c514ef1c6b50c705a03f9c129824dff"
},
"downloads": -1,
"filename": "py_singleline-1.0.0a2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "39d5c381800aa225b3db381f11042b90",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 35351,
"upload_time": "2023-09-21T07:43:06",
"upload_time_iso_8601": "2023-09-21T07:43:06.704000Z",
"url": "https://files.pythonhosted.org/packages/1a/5f/ead373ff97cadd1ea11e8aef92ccadca33be68993e2abbb2fc9ec4394ec3/py_singleline-1.0.0a2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "99213c3fd9b0be797d145b7e1f39dfac57814234045ea166235983586d6d48f0",
"md5": "f804e5cadb723f96096fd2f4c12d2945",
"sha256": "c9cd3dc906dbb2ad6c0abdb5df8b6ef135dc92419cc52ca3a0ffde131da502d9"
},
"downloads": -1,
"filename": "py-singleline-1.0.0a2.tar.gz",
"has_sig": false,
"md5_digest": "f804e5cadb723f96096fd2f4c12d2945",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 27143,
"upload_time": "2023-09-21T07:43:08",
"upload_time_iso_8601": "2023-09-21T07:43:08.644508Z",
"url": "https://files.pythonhosted.org/packages/99/21/3c3fd9b0be797d145b7e1f39dfac57814234045ea166235983586d6d48f0/py-singleline-1.0.0a2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-09-21 07:43:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "whisperity",
"github_project": "py-singleline",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "py-singleline"
}