Skip to content

Commit

Permalink
Merge pull request #326 from CycloneDX/callable-module
Browse files Browse the repository at this point in the history
feat: make package/module callable
  • Loading branch information
madpah authored Apr 5, 2022
2 parents b647219 + 2bac83a commit 193f1a4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 7 deletions.
20 changes: 20 additions & 0 deletions cyclonedx_py/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# encoding: utf-8

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.

from .client import main

main(prog_name=__package__)
9 changes: 5 additions & 4 deletions cyclonedx_py/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import os
import sys
from datetime import datetime
from typing import Optional

from cyclonedx.model import Tool
from cyclonedx.model.bom import Bom
Expand Down Expand Up @@ -122,8 +123,8 @@ def execute(self) -> None:
output.output_to_file(filename=output_filename, allow_overwrite=self._arguments.output_file_overwrite)

@staticmethod
def get_arg_parser() -> argparse.ArgumentParser:
arg_parser = argparse.ArgumentParser(description='CycloneDX SBOM Generator')
def get_arg_parser(*, prog: Optional[str] = None) -> argparse.ArgumentParser:
arg_parser = argparse.ArgumentParser(prog=prog, description='CycloneDX SBOM Generator')

input_group = arg_parser.add_mutually_exclusive_group(required=True)
input_group.add_argument(
Expand Down Expand Up @@ -254,8 +255,8 @@ def _get_input_parser(self) -> BaseParser:
raise CycloneDxCmdException('Parser type could not be determined.')


def main() -> None:
parser = CycloneDxCmd.get_arg_parser()
def main(*, prog_name: Optional[str] = None) -> None:
parser = CycloneDxCmd.get_arg_parser(prog=prog_name)
args = parser.parse_args()
CycloneDxCmd(args).execute()

Expand Down
14 changes: 11 additions & 3 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ Usage
Command Line Usage
------------------

Once installed, you can access the full documentation by running ``--help``:
Once installed, you can call the tool via the following methods:

.. code-block:: bash
$ python3 -m cyclonedx_py
$ cyclonedx-py
$ cyclonedx-bom
The full documentation can be issued by running with ``--help``:

.. code-block:: bash
Expand Down Expand Up @@ -215,8 +223,8 @@ Requirements
* :py:mod:`cyclonedx_py.parser.requirements.RequirementsParser`: Parses a multiline string that you provide that conforms
to the ``requirements.txt`` :pep:`508` standard.
* :py:mod:`cyclonedx_py.parser.requirements.RequirementsFileParser`: Parses a file that you provide the path to that conforms to the ``requirements.txt`` :pep:`508` standard. It supports nested
files, so if there is a line in your ``requirements.txt`` file with the ``-r requirements-nested.txt`` syntax, it'll parse the nested file as part of the same file.
* :py:mod:`cyclonedx_py.parser.requirements.RequirementsFileParser`: Parses a file that you provide the path to that conforms to the ``requirements.txt`` :pep:`508` standard.
It supports nested files, so if there is a line in your ``requirements.txt`` file with the ``-r requirements-nested.txt`` syntax, it'll parse the nested file as part of the same file.
CycloneDX software bill-of-materials require pinned versions of requirements. If your `requirements.txt` does not have
pinned versions, warnings will be recorded and the dependencies without pinned versions will be excluded from the
Expand Down
18 changes: 18 additions & 0 deletions tests/integration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# encoding: utf-8

# This file is part of CycloneDX Python Lib
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.
40 changes: 40 additions & 0 deletions tests/integration/test_can_call_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# encoding: utf-8

# This file is part of CycloneDX Python Lib
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.

import subprocess
import sys
from unittest import TestCase

import cyclonedx_py


class TestCli(TestCase):

def test_callable_as_module(self) -> None:
args = [sys.executable, '-m', cyclonedx_py.__name__, '--help']

# Test whether the call passed, is fair enough for now.
# Additional tests may come later, to check output etc.
returncode = subprocess.call(
args,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
shell=False,
)

self.assertEqual(0, returncode, msg='subprocess returned unexpected non-zero')

0 comments on commit 193f1a4

Please sign in to comment.