Skip to content

Commit

Permalink
Python 3.8 backport and minor code cleanup (#1)
Browse files Browse the repository at this point in the history
* Backport to Python 3.8

Only requires adding a few `from __future__ import annotations`

Signed-off-by: Bolun Thompson <[email protected]>

* Add runnable tests

Signed-off-by: Bolun Thompson <[email protected]>

* Add github action for tests

Signed-off-by: Bolun Thompson <[email protected]>

* Update submodule to use pash libbash

Signed-off-by: Bolun Thompson <[email protected]>

* Fix simple type-hint errors

Signed-off-by: Bolun Thompson <[email protected]>

* Format with black

Signed-off-by: Bolun Thompson <[email protected]>

* Remove editable

---------

Signed-off-by: Bolun Thompson <[email protected]>
  • Loading branch information
BolunThompson authored Dec 17, 2024
1 parent 3cfd4f0 commit 5f79dea
Show file tree
Hide file tree
Showing 14 changed files with 473 additions and 354 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Test
on:
pull_request_target:
types: [assigned, opened, synchronize, reopened, ready_for_review]
paths:
- libbash/**
push:
branches:
- main
paths:
- libbash/**
jobs:
LibBash-Test:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
runs-on: ${{ matrix.os }}
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Running Tests
run: |
python3 -m venv venv
. venv/bin/activate
./setup_test.sh
./test.py | tee python.log
test_succ=$?
# TODO: Is this working?
timer="$(LANG=en_us_88591; date)"
echo "VERSION<<EOF" >> "$GITHUB_ENV"
echo "OS:${{matrix.os}}" >> "$GITHUB_ENV"
echo "$timer" >> "$GITHUB_ENV"
cat python.log >> "$GITHUB_ENV"
echo 'EOF' >> "$GITHUB_ENV"
exit $test_succ
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "bash-5.2"]
path = libbash/bash-5.2
url = https://github.com/sethsabar/bash-for-libbash.git
url = https://github.com/binpash/bash-for-libbash
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**NOTE: This project is mostly functional, however there are a few minor bugs with the Bash source code causing issues with our API. Take a look at `test.py` to see which tests we are currently not testing because they will fail!**

`libbash` can be installed via pip: https://pypi.org/project/libbash/
`libbash` can be installed via pip: https://pypi.org/project/libbash/

## API

Expand Down Expand Up @@ -44,8 +44,8 @@ while [ $counter -le 5 ]; do
done
```

Whether `while` is aliased or not depends on the time of day that the script is run, and this affects the functionality of the `while` loop. This is because alias expansion is done
*before* parsing in Bash. As this example shows, determining alias expansions is not possible without executing a Bash script. Therefore, one can not expect any uses of `alias` or
Whether `while` is aliased or not depends on the time of day that the script is run, and this affects the functionality of the `while` loop. This is because alias expansion is done
*before* parsing in Bash. As this example shows, determining alias expansions is not possible without executing a Bash script. Therefore, one can not expect any uses of `alias` or
other programs that change the script before parse-time to be reflected.

## Additional Documents
Expand Down
3 changes: 0 additions & 3 deletions libbash/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
from .api import ast_to_json, bash_to_ast, ast_to_bash
from .test import run_tests


49 changes: 28 additions & 21 deletions libbash/api.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
from __future__ import annotations

from .bash_command import *
import ctypes
import os

from typing import Any

# current location + ../../bash-5.2/bash.so
BASH_FILE_PATH = os.path.join(os.path.dirname(
__file__), "bash-5.2", "bash.so")
BASH_FILE_PATH = os.path.join(os.path.dirname(__file__), "bash-5.2", "bash.so")


def _setup_bash() -> ctypes.CDLL:
if not os.path.isfile(BASH_FILE_PATH):
# run configure and make clean all
# this will compile the bash source code into a shared object file
# that can be called from python using ctypes
result = os.system("cd " + os.path.dirname(BASH_FILE_PATH) +
" && ./configure && make clean all")
result = os.system(
"cd "
+ os.path.dirname(BASH_FILE_PATH)
+ " && ./configure && make clean all"
)
if result != 0:
raise Exception("Bash compilation failed")

Expand All @@ -22,8 +29,7 @@ def _setup_bash() -> ctypes.CDLL:
try:
bash = ctypes.CDLL(BASH_FILE_PATH)
except OSError:
raise Exception(
"Bash shared object file not found at path: " + BASH_FILE_PATH)
raise Exception("Bash shared object file not found at path: " + BASH_FILE_PATH)

# tell python arg types and return type of the initialize_shell_libbash
bash.initialize_shell_libbash.argtypes = []
Expand Down Expand Up @@ -56,13 +62,14 @@ def ast_to_bash(ast: list[Command], write_to: str):
for comm in ast:
command_string = bash.make_command_string(comm._to_ctypes())
bash_str += command_string
bash_str += "\n".encode('utf-8')
bash_str += "\n".encode("utf-8")

with open(write_to, "wb") as f:
# don't decode the bytes, just write them to the file
f.write(bash_str)

def ast_to_json(ast: list[Command]) -> list[dict[str, any]]:

def ast_to_json(ast: list[Command]) -> list[dict[str, Any]]:
"""
Converts the AST to a JSON style object.
:param ast: The AST, a list of Command objects.
Expand All @@ -71,8 +78,9 @@ def ast_to_json(ast: list[Command]) -> list[dict[str, any]]:
return [command._to_json() for command in ast]


def bash_to_ast(bash_file: str, with_linno_info: bool=False) -> \
[list[Command], list[Command, bytes, int, int]]:
def bash_to_ast(
bash_file: str, with_linno_info: bool = False
) -> list[Command] | list[tuple[Command, bytes, int, int]]:
"""
Extracts the AST from the bash source code.
Uses ctypes to call an injected bash function that returns the AST.
Expand All @@ -90,7 +98,7 @@ def bash_to_ast(bash_file: str, with_linno_info: bool=False) -> \
bash.set_bash_file.restype = ctypes.c_int

# call the function
set_result: ctypes.c_int = bash.set_bash_file(bash_file.encode('utf-8'))
set_result: int = bash.set_bash_file(bash_file.encode("utf-8"))
if set_result < 0:
raise IOError("Setting bash file failed")

Expand All @@ -108,22 +116,21 @@ def bash_to_ast(bash_file: str, with_linno_info: bool=False) -> \

while True:
# call the function
linno_before: int = ctypes.c_int.in_dll(bash, 'line_number').value
linno_before: int = ctypes.c_int.in_dll(bash, "line_number").value
read_result: ctypes.c_int = bash.read_command_safe()
linno_after: int = ctypes.c_int.in_dll(bash, 'line_number').value
linno_after: int = ctypes.c_int.in_dll(bash, "line_number").value
if read_result != 0:
bash.unset_bash_input(0)
raise RuntimeError(
"Bash read command failed, shell script may be invalid")
raise RuntimeError("Bash read command failed, shell script may be invalid")

# read the global_command variable
global_command: ctypes.POINTER(c_bash.command) = ctypes.POINTER(
c_bash.command).in_dll(bash, 'global_command')
global_command: ctypes._Pointer[c_bash.command] = ctypes.POINTER(
c_bash.command
).in_dll(bash, "global_command")

# global_command is null
if not global_command:
eof_reached: ctypes.c_int = ctypes.c_int.in_dll(
bash, 'EOF_Reached')
eof_reached: ctypes.c_int = ctypes.c_int.in_dll(bash, "EOF_Reached")
if eof_reached:
bash.unset_bash_input(0)
break
Expand All @@ -136,9 +143,9 @@ def bash_to_ast(bash_file: str, with_linno_info: bool=False) -> \

# add the command to the list
if with_linno_info:
command_string = b''.join(lines[linno_before:linno_after])
command_string = b"".join(lines[linno_before:linno_after])
command_list.append((command, command_string, linno_before, linno_after))
else:
command_list.append(command)

return command_list
return command_list
2 changes: 1 addition & 1 deletion libbash/bash-5.2
Loading

0 comments on commit 5f79dea

Please sign in to comment.