From 60cccfaaa7f553592497ef34a0fc909fff81952b Mon Sep 17 00:00:00 2001 From: Josh Cannon <3956745+thejcannon@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:25:26 -0500 Subject: [PATCH 1/4] Add --- AUTHORS.txt | 1 + cogapp/cogapp.py | 14 +++++++++-- cogapp/test_cogapp.py | 54 +++++++++++++++++++++++++++++++++++++++++++ docs/module.rst | 4 ++++ 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/AUTHORS.txt b/AUTHORS.txt index 8762ecf..8890ad4 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -9,6 +9,7 @@ Daniel Murdin Doug Hellmann Hugh Perkins Jean-François Giraud +Joshua Cannon Panayiotis Gavriil Petr Gladkiy Phil Kirkpatrick diff --git a/cogapp/cogapp.py b/cogapp/cogapp.py index 972e900..f895e45 100644 --- a/cogapp/cogapp.py +++ b/cogapp/cogapp.py @@ -7,6 +7,7 @@ import io import linecache import os +import os.path import re import shlex import sys @@ -111,10 +112,11 @@ class CogCheckFailed(CogError): class CogGenerator(Redirectable): """A generator pulled from a source file.""" - def __init__(self, options=None): + def __init__(self, filename, options=None): super().__init__() self.markers = [] self.lines = [] + self.filename = filename self.options = options or CogOptions() def parse_marker(self, line): @@ -152,6 +154,7 @@ def evaluate(self, cog, globals, fname): cog.cogmodule.msg = self.msg cog.cogmodule.out = self.out cog.cogmodule.outl = self.outl + cog.cogmodule.include = self.include cog.cogmodule.error = self.error real_stdout = sys.stdout @@ -205,6 +208,13 @@ def outl(self, sOut="", **kw): self.out(sOut, **kw) self.out("\n") + def include(self, filepath, **kw): + """The cog.include function.""" + parent_dir = os.path.dirname(self.filename) + with open(os.path.join(parent_dir, filepath), 'r') as f: + self.out(f.read(), **kw) + self.out("\n") + def error(self, msg="Error raised by cog generator."): """The cog.error function. @@ -466,7 +476,7 @@ def process_file(self, file_in, file_out, fname=None, globals=None): file_out.write(line) # `line` is the begin spec - gen = CogGenerator(options=self.options) + gen = CogGenerator(filename=file_name_in, options=self.options) gen.set_output(stdout=self.stdout) gen.parse_marker(line) first_line_num = file_in.linenumber() diff --git a/cogapp/test_cogapp.py b/cogapp/test_cogapp.py index 9a394b7..dc9b99f 100644 --- a/cogapp/test_cogapp.py +++ b/cogapp/test_cogapp.py @@ -1449,6 +1449,58 @@ def test_change_dir(self): output = self.output.getvalue() self.assertIn("(changed)", output) + def test_cog_include(self): + d = { + "coglet": "World!", + "sub": { + "coglet": "Hello!", + "test.cog": """\ + //[[[cog + cog.include("coglet") + cog.include("./coglet") + cog.include("../coglet") + //]]] + //[[[end]]] + """, + "test.out": """\ + //[[[cog + cog.include("coglet") + cog.include("./coglet") + cog.include("../coglet") + //]]] + Hello! + Hello! + World! + //[[[end]]] + """, + }, + } + + make_files(d) + self.cog.callable_main(["argv0", "-r", "sub/test.cog"]) + self.assertFilesSame("sub/test.cog", "sub/test.out") + output = self.output.getvalue() + self.assertIn("(changed)", output) + + + def test_cog_include_with_stdin(self): + d = {"coglet": "Hello World!"} + make_files(d) + stdin = io.StringIO("--[[[cog cog.include('coglet') ]]]\n--[[[end]]]\n") + + def restore_stdin(old_stdin): + sys.stdin = old_stdin + + self.addCleanup(restore_stdin, sys.stdin) + sys.stdin = stdin + + stderr = io.StringIO() + self.cog.set_output(stderr=stderr) + self.cog.callable_main(["argv0", "-"]) + output = self.output.getvalue() + outerr = stderr.getvalue() + self.assertEqual(output, "--[[[cog cog.include('coglet') ]]]\nHello World!\n--[[[end]]]\n") + self.assertEqual(outerr, "") class CogTestLineEndings(TestCaseWithTempDir): """Tests for -U option (force LF line-endings in output).""" @@ -2817,5 +2869,7 @@ def test_validate_hash_with_base64_match(self): self.assertTrue(result) + + # Things not yet tested: # - A bad -w command (currently fails silently). diff --git a/docs/module.rst b/docs/module.rst index 6885058..702b173 100644 --- a/docs/module.rst +++ b/docs/module.rst @@ -25,6 +25,10 @@ The module contents are: **cog.outl** Same as **cog.out**, but adds a trailing newline. +**cog.include(filepath=, **kwargs)** + Outputs the contents of the file at filepath (relative to the current file's directory). + (Keyword-arguments forwarded to **cog.out**) + **cog.msg** `(msg)` Prints `msg` to stdout with a "Message: " prefix. From cdbe54d3d30af8d52b7d32da16e1ddcb11d30685 Mon Sep 17 00:00:00 2001 From: Josh Cannon <3956745+thejcannon@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:51:42 -0500 Subject: [PATCH 2/4] okie dokie --- .github/workflows/ci.yml | 38 +++++++++++++++---- .github/workflows/steps/checkout.yml | 4 ++ .../workflows/steps/install-dependencies.yml | 3 ++ .github/workflows/steps/ruff.yml | 1 + .github/workflows/steps/setup-python.yml | 2 + cogapp/cogapp.py | 3 +- cogapp/test_cogapp.py | 8 ++-- uv.lock | 7 ++++ 8 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/steps/checkout.yml create mode 100644 .github/workflows/steps/install-dependencies.yml create mode 100644 .github/workflows/steps/ruff.yml create mode 100644 .github/workflows/steps/setup-python.yml create mode 100644 uv.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec53e1c..cd91b63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,38 +16,50 @@ concurrency: cancel-in-progress: true jobs: - format: name: "Check formatting" runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + # [[[cog cog.include("./steps/checkout.yml") ]]] + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + # [[[end]]] + # [[[cog cog.include("./steps/ruff.yml") ]]] - uses: astral-sh/ruff-action@eaf0ecdd668ceea36159ff9d91882c9795d89b49 # v3.4.0 + # [[[end]]] with: - args: 'format --check' + args: "format --check" lint: name: "Lint" runs-on: ubuntu-latest steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + # [[[cog cog.include("./steps/checkout.yml") ]]] + - name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + # [[[end]]] + # [[[cog cog.include("./steps/ruff.yml") ]]] - uses: astral-sh/ruff-action@eaf0ecdd668ceea36159ff9d91882c9795d89b49 # v3.4.0 + # [[[end]]] + # [[[cog cog.include("./steps/setup-python.yml") ]]] - name: "Set up Python" uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + # [[[end]]] with: python-version: "3.13" + # [[[cog cog.include("./steps/install-dependencies.yml") ]]] - name: "Install dependencies" run: | python -m pip install -r requirements.pip - + # [[[end]]] - name: "Check the docs are up-to-date" run: | make lintdoc @@ -79,20 +91,25 @@ jobs: os-version: "13" steps: + # [[[cog cog.include("./steps/checkout.yml") ]]] - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false + # [[[end]]] + # [[[cog cog.include("./steps/setup-python.yml") ]]] - name: "Set up Python" uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + # [[[end]]] with: python-version: "${{ matrix.python }}" + # [[[cog cog.include("./steps/install-dependencies.yml") ]]] - name: "Install dependencies" run: | python -m pip install -r requirements.pip - + # [[[end]]] - name: "Run tox for ${{ matrix.python }}" run: | python -m tox @@ -111,21 +128,26 @@ jobs: runs-on: ubuntu-latest steps: + # [[[cog cog.include("./steps/checkout.yml") ]]] - name: "Check out the repo" uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - fetch-depth: "0" persist-credentials: false + # [[[end]]] + fetch-depth: "0" + # [[[cog cog.include("./steps/setup-python.yml") ]]] - name: "Set up Python" uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + # [[[end]]] with: python-version: "3.9" + # [[[cog cog.include("./steps/install-dependencies.yml") ]]] - name: "Install dependencies" run: | python -m pip install -r requirements.pip - + # [[[end]]] - name: "Download coverage data" uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: diff --git a/.github/workflows/steps/checkout.yml b/.github/workflows/steps/checkout.yml new file mode 100644 index 0000000..42d23b5 --- /dev/null +++ b/.github/workflows/steps/checkout.yml @@ -0,0 +1,4 @@ +- name: "Check out the repo" + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + persist-credentials: false diff --git a/.github/workflows/steps/install-dependencies.yml b/.github/workflows/steps/install-dependencies.yml new file mode 100644 index 0000000..77b8255 --- /dev/null +++ b/.github/workflows/steps/install-dependencies.yml @@ -0,0 +1,3 @@ +- name: "Install dependencies" + run: | + python -m pip install -r requirements.pip diff --git a/.github/workflows/steps/ruff.yml b/.github/workflows/steps/ruff.yml new file mode 100644 index 0000000..fd0d36e --- /dev/null +++ b/.github/workflows/steps/ruff.yml @@ -0,0 +1 @@ +- uses: astral-sh/ruff-action@eaf0ecdd668ceea36159ff9d91882c9795d89b49 # v3.4.0 diff --git a/.github/workflows/steps/setup-python.yml b/.github/workflows/steps/setup-python.yml new file mode 100644 index 0000000..f98f779 --- /dev/null +++ b/.github/workflows/steps/setup-python.yml @@ -0,0 +1,2 @@ +- name: "Set up Python" + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 diff --git a/cogapp/cogapp.py b/cogapp/cogapp.py index f895e45..a6a1d34 100644 --- a/cogapp/cogapp.py +++ b/cogapp/cogapp.py @@ -211,9 +211,8 @@ def outl(self, sOut="", **kw): def include(self, filepath, **kw): """The cog.include function.""" parent_dir = os.path.dirname(self.filename) - with open(os.path.join(parent_dir, filepath), 'r') as f: + with open(os.path.join(parent_dir, filepath), "r") as f: self.out(f.read(), **kw) - self.out("\n") def error(self, msg="Error raised by cog generator."): """The cog.error function. diff --git a/cogapp/test_cogapp.py b/cogapp/test_cogapp.py index dc9b99f..913c01e 100644 --- a/cogapp/test_cogapp.py +++ b/cogapp/test_cogapp.py @@ -1482,7 +1482,6 @@ def test_cog_include(self): output = self.output.getvalue() self.assertIn("(changed)", output) - def test_cog_include_with_stdin(self): d = {"coglet": "Hello World!"} make_files(d) @@ -1499,9 +1498,12 @@ def restore_stdin(old_stdin): self.cog.callable_main(["argv0", "-"]) output = self.output.getvalue() outerr = stderr.getvalue() - self.assertEqual(output, "--[[[cog cog.include('coglet') ]]]\nHello World!\n--[[[end]]]\n") + self.assertEqual( + output, "--[[[cog cog.include('coglet') ]]]\nHello World!\n--[[[end]]]\n" + ) self.assertEqual(outerr, "") + class CogTestLineEndings(TestCaseWithTempDir): """Tests for -U option (force LF line-endings in output).""" @@ -2869,7 +2871,5 @@ def test_validate_hash_with_base64_match(self): self.assertTrue(result) - - # Things not yet tested: # - A bad -w command (currently fails silently). diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..28ab00a --- /dev/null +++ b/uv.lock @@ -0,0 +1,7 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" + +[[package]] +name = "cogapp" +source = { editable = "." } From 6b22f22547d1251305ea92a06ed8be0ba8a3ab60 Mon Sep 17 00:00:00 2001 From: Josh Cannon <3956745+thejcannon@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:51:54 -0500 Subject: [PATCH 3/4] undo this --- uv.lock | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 uv.lock diff --git a/uv.lock b/uv.lock deleted file mode 100644 index 28ab00a..0000000 --- a/uv.lock +++ /dev/null @@ -1,7 +0,0 @@ -version = 1 -revision = 2 -requires-python = ">=3.9" - -[[package]] -name = "cogapp" -source = { editable = "." } From ddf0a36c568fe9ac0124b28edf3aa8b135b2c9a3 Mon Sep 17 00:00:00 2001 From: Josh Cannon <3956745+thejcannon@users.noreply.github.com> Date: Tue, 8 Jul 2025 22:00:16 -0500 Subject: [PATCH 4/4] add check step --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd91b63..52c069b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,14 @@ jobs: run: | make lintdoc + - name: "Check that this file is up-to-date" + run: | + python -m cogapp -r --check --diff .github/workflows/ci.yml + if [ $? -ne 0 ]; then + echo 'Docs need to be updated: `python -m cogapp -r .github/workflows/ci.yml`'; + exit 1; + fi + tests: name: "Python ${{ matrix.python }} on ${{ matrix.os }}" runs-on: "${{ matrix.os }}-${{ matrix.os-version || 'latest' }}"