diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 566dada..13ef1bd 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -7,7 +7,6 @@ on: push: branches: [ "master" ] pull_request: - branches: [ "master" ] jobs: build: @@ -16,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v3 @@ -27,9 +26,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install flake8 pytest - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - if [ -f requirements_dev.txt ]; then pip install -r requirements_dev.txt; fi + pip install -e .[tests] - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names @@ -40,5 +37,26 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - pytest --cov=excelAddinGenerator + pytest --cov=src coveralls --service=github + + flake8: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.13"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e .[tests] + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --show-source --statistics diff --git a/excelAddinGenerator/__init__.py b/excelAddinGenerator/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/excelAddinGenerator/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/excelAddinGenerator/main.py b/excelAddinGenerator/main.py deleted file mode 100644 index 91cc16c..0000000 --- a/excelAddinGenerator/main.py +++ /dev/null @@ -1,44 +0,0 @@ -import shutil, sys, os, zipfile - -def main(args): - if len(args) > 2: - # check the extension on sys.argv[1] to determine which function to call - input_file = args[1] - output_file_name = args[2] - if input_file.endswith('.xlam'): - createFromZip(input_file, args[0] + '/../src/data', output_file_name) - elif input_file.endswith('.bin'): - createFromBin(input_file, args[0] + '/../src/data', output_file_name) - else: - raise Exception(input_file, " is not a valid file format.") - -def createFromBin(input_file, wrapper_dir, output_file_name): - """Create a zip file containing the provided bin""" - # file must start with 'd0 cf 11 e0 a1 b1 1a e1' - fileSig = open(input_file, "rb").read(8).hex() - if fileSig != 'd0cf11e0a1b11ae1': - raise Exception('File signature {} is not as expected.', format(fileSig)) - shutil.copy(input_file, wrapper_dir + "/xl/vbaProject.bin") - shutil.make_archive(output_file_name, 'zip', wrapper_dir) - shutil.move(output_file_name + ".zip", output_file_name) - -def createFromZip(input_file, wrapper_dir, output_file_name): - """Create a zip file containing the bin file within the provided zip file""" - extractBinFromZip(input_file) - createFromBin('xl/vbaProject.bin', wrapper_dir, output_file_name) - -def extractBinFromZip(input_file): - # check that input is a zip file - if zipfile.is_zipfile(input_file): - # check that the zip archive contains /xl/vbaProject.bin - with zipfile.ZipFile(input_file, 'r') as zip: - zip.extract('xl/vbaProject.bin') - else: - raise Exception(input_file, " is not a valid file format.") - -if __name__ == "__main__": - args = [] - args[0] = os.path.dirname(sys.argv[0]) - args[1] = sys.argv[1] - args[2] = sys.argv[2] - main(args) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b6d9bfd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,37 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "excel_addin_generator" +version = "0.0.1" +authors = [ + { name="Kevin Nowaczyk", email="beakerboy99@yahoo.com" }, +] +description = "Create an Excel addin with python." +readme = "README.md" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +[project.optional-dependencies] +tests = [ + 'coveralls', + 'filehash', + 'flake8-annotations', + 'pytest', + 'pytest-cov' +] +[project.urls] +"Homepage" = "https://github.com/Beakerboy/vbaProject-Compiler" +"Bug Tracker" = "https://github.com/Beakerboy/vbaProject-Compiler/issues" + +[tool.pytest.ini_options] +pythonpath = "src" +testpaths = [ + "tests", +] +[tool.setuptools.package-data] +"*" = ["*.xml", ".rels", "*.rels"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 7a3ca67..0000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -from setuptools import setup - -setup( - name="excelAddinGenerator", - packages=['excelAddinGenerator'], - tests_require=['pytest'], -) diff --git a/src/excel_addin_generator/__init__.py b/src/excel_addin_generator/__init__.py new file mode 100644 index 0000000..fbf40fc --- /dev/null +++ b/src/excel_addin_generator/__init__.py @@ -0,0 +1 @@ +# None diff --git a/src/excel_addin_generator/__main__.py b/src/excel_addin_generator/__main__.py new file mode 100644 index 0000000..ce1c907 --- /dev/null +++ b/src/excel_addin_generator/__main__.py @@ -0,0 +1,24 @@ +import argparse +import os +import sys +import excel_addin_generator.main as gen + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("input", + help="The input bin or xlam file.") + parser.add_argument("output", + help="The output path and file name.") + args = parser.parse_args() + base_path = os.path.dirname(__file__) + # check the extension on sys.argv[1] to determine which function to call + if args.input.endswith('.xlam'): + gen.createFromZip(args.input, base_path + '/data', args.output) + elif args.input.endswith('.bin'): + gen.create_from_bin(args.input, base_path + '/data', args.output) + else: + raise Exception(args.input, " is not a valid file format.") + + +main() diff --git a/src/data/[Content_Types].xml b/src/excel_addin_generator/data/[Content_Types].xml similarity index 100% rename from src/data/[Content_Types].xml rename to src/excel_addin_generator/data/[Content_Types].xml diff --git a/src/data/_rels/.rels b/src/excel_addin_generator/data/_rels/.rels similarity index 90% rename from src/data/_rels/.rels rename to src/excel_addin_generator/data/_rels/.rels index baedf26..bed977b 100644 --- a/src/data/_rels/.rels +++ b/src/excel_addin_generator/data/_rels/.rels @@ -1,4 +1,4 @@ - + diff --git a/src/data/docProps/app.xml b/src/excel_addin_generator/data/docProps/app.xml similarity index 100% rename from src/data/docProps/app.xml rename to src/excel_addin_generator/data/docProps/app.xml diff --git a/src/data/docProps/core.xml b/src/excel_addin_generator/data/docProps/core.xml similarity index 100% rename from src/data/docProps/core.xml rename to src/excel_addin_generator/data/docProps/core.xml diff --git a/src/data/xl/_rels/workbook.xml.rels b/src/excel_addin_generator/data/xl/_rels/workbook.xml.rels similarity index 100% rename from src/data/xl/_rels/workbook.xml.rels rename to src/excel_addin_generator/data/xl/_rels/workbook.xml.rels diff --git a/src/data/xl/styles.xml b/src/excel_addin_generator/data/xl/styles.xml similarity index 100% rename from src/data/xl/styles.xml rename to src/excel_addin_generator/data/xl/styles.xml diff --git a/src/data/xl/theme/theme1.xml b/src/excel_addin_generator/data/xl/theme/theme1.xml similarity index 100% rename from src/data/xl/theme/theme1.xml rename to src/excel_addin_generator/data/xl/theme/theme1.xml diff --git a/src/data/xl/workbook.xml b/src/excel_addin_generator/data/xl/workbook.xml similarity index 100% rename from src/data/xl/workbook.xml rename to src/excel_addin_generator/data/xl/workbook.xml diff --git a/src/data/xl/worksheets/sheet1.xml b/src/excel_addin_generator/data/xl/worksheets/sheet1.xml similarity index 100% rename from src/data/xl/worksheets/sheet1.xml rename to src/excel_addin_generator/data/xl/worksheets/sheet1.xml diff --git a/src/excel_addin_generator/main.py b/src/excel_addin_generator/main.py new file mode 100644 index 0000000..4e02ff6 --- /dev/null +++ b/src/excel_addin_generator/main.py @@ -0,0 +1,34 @@ +import os +import shutil +import sys +import zipfile + + +def create_from_bin(input_file: str, wrapper_dir: str, output_file_name: str) -> None: + + """Create a zip file containing the provided bin""" + # file must start with 'd0 cf 11 e0 a1 b1 1a e1' + fileSig = open(input_file, "rb").read(8).hex() + if fileSig != 'd0cf11e0a1b11ae1': + raise Exception('File signature {} is not as expected.', format(fileSig)) + shutil.copy(input_file, wrapper_dir + "/xl/vbaProject.bin") + shutil.make_archive(output_file_name, 'zip', wrapper_dir) + shutil.move(output_file_name + ".zip", output_file_name) + + +def createFromZip(input_file: str, wrapper_dir: str, output_file_name: str) -> None: + + """Create a zip file containing the bin file within the provided zip file""" + extractBinFromZip(input_file) + create_from_bin('xl/vbaProject.bin', wrapper_dir, output_file_name) + + +def extractBinFromZip(input_file: str) -> None: + + # check that input is a zip file + if zipfile.is_zipfile(input_file): + # check that the zip archive contains /xl/vbaProject.bin + with zipfile.ZipFile(input_file, 'r') as zip: + zip.extract('xl/vbaProject.bin') + else: + raise Exception(input_file, " is not a valid file format.") diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/test_excelAddinGenerator.py b/tests/test_excelAddinGenerator.py index 3af18af..98ae9d4 100644 --- a/tests/test_excelAddinGenerator.py +++ b/tests/test_excelAddinGenerator.py @@ -1,41 +1,34 @@ # test_excelAddinGenerator.py - +import excel_addin_generator.main import pytest -from excelAddinGenerator.main import * from os.path import exists from filehash import FileHash -def test_success_from_bin(): + +def test_success_from_bin() -> None: """Test that xlam is successfully generated from a OLE file""" - createFromBin("tests/vbaProject.bin", "src/data", "success_bin.xlam") + excel_addin_generator.main.create_from_bin("tests/vbaProject.bin", "src/excel_addin_generator/data", "success_bin.xlam") # Assert that xlam file is created assert exists("success_bin.xlam") - #assert that bin file within success_bin.xlam matches tests/vbaProject.bin - extractBinFromZip("success_bin.xlam") + # assert that bin file within success_bin.xlam matches tests/vbaProject.bin + excel_addin_generator.main.extractBinFromZip("success_bin.xlam") md5hasher = FileHash('md5') assert md5hasher.hash_file("tests/vbaProject.bin") == md5hasher.hash_file("xl/vbaProject.bin") - createFromZip("success_bin.xlam", "src/data", "success_xlam.xlam") + excel_addin_generator.main.createFromZip("success_bin.xlam", "src/excel_addin_generator/data", "success_xlam.xlam") assert exists("success_xlam.xlam") - #assert that bin file within success_xlam.xlam matches bin file within success_bin.xlam - extractBinFromZip("success_xlam.xlam") + # assert that bin file within success_xlam.xlam matches bin file within success_bin.xlam + excel_addin_generator.main.extractBinFromZip("success_xlam.xlam") assert md5hasher.hash_file("tests/vbaProject.bin") == md5hasher.hash_file("xl/vbaProject.bin") - -def test_not_bin_exception(): + + +def test_not_bin_exception() -> None: """ Test that an exception is thrown if the bin file is not an OLE file""" - with pytest.raises(Exception) as e_info: - createFromBin("tests/blank.bin", "src/data", "./fail.xlam") - -def test_xlam_not_zip(): - """ Test that an exception is thrown if the zip is not a zip archive""" - with pytest.raises(Exception) as e_info: - createFromZip("tests/blank.bin", "src/data", "./fail.xlam") + with pytest.raises(Exception): + excel_addin_generator.main.createFromBin("tests/blank.bin", "src/excel_addin_generator/data", "./fail.xlam") -def test_main(): - main(["./excelAddinGenerator", "./tests/vbaProject.bin", "success_bin.xlam"]) - main(["./excelAddinGenerator", "success_bin.xlam", "success_xlam.xlam"]) -def test_main_incorrect_type(): +def test_xlam_not_zip() -> None: """ Test that an exception is thrown if the zip is not a zip archive""" - with pytest.raises(Exception) as e_info: - main(["./excelAddinGenerator", "./src/data/xl/styles.xml", "fail.xlam"]) + with pytest.raises(Exception): + excel_addin_generator.main.createFromZip("tests/blank.bin", "src/excel_addin_generator/data", "./fail.xlam")