{"id":13501381,"url":"https://github.com/usagitoneko97/klara","last_synced_at":"2025-03-29T09:30:25.305Z","repository":{"id":37471215,"uuid":"125526736","full_name":"usagitoneko97/klara","owner":"usagitoneko97","description":"Automatic test case generation for python and static analysis library","archived":false,"fork":false,"pushed_at":"2022-03-28T08:41:17.000Z","size":9576,"stargazers_count":261,"open_issues_count":3,"forks_count":13,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-03T12:06:08.162Z","etag":null,"topics":["ast","cfg","python","ssa","static-analysis","static-code-analysis"],"latest_commit_sha":null,"homepage":"https://klara-py.readthedocs.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/usagitoneko97.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE.rst","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-16T14:24:03.000Z","updated_at":"2025-02-19T02:18:48.000Z","dependencies_parsed_at":"2022-08-02T01:09:38.095Z","dependency_job_id":null,"html_url":"https://github.com/usagitoneko97/klara","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usagitoneko97%2Fklara","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usagitoneko97%2Fklara/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usagitoneko97%2Fklara/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usagitoneko97%2Fklara/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usagitoneko97","download_url":"https://codeload.github.com/usagitoneko97/klara/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246166884,"owners_count":20734354,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","cfg","python","ssa","static-analysis","static-code-analysis"],"created_at":"2024-07-31T22:01:35.224Z","updated_at":"2025-03-29T09:30:24.765Z","avatar_url":"https://github.com/usagitoneko97.png","language":"Python","readme":"# Klara\n\nKlara is a static analysis tools to automatic generate test case, based\non SMT (z3) solver, with a powerful ast level inference system. Klara\nwill take python file as input and generate corresponding test file in\npytest format, that attempt to cover all return values. For example,\nfollowing function in file `test.py`\n\n``` python\ndef triangle(x: int, y: int, z: int) -\u003e str:\n    if x == y == z:\n        return \"Equilateral triangle\"\n    elif x == y or y == z or x == z:\n        return \"Isosceles triangle\"\n    else:\n        return \"Scalene triangle\"\n```\n\nwill generate\n\n``` python\nimport test\ndef test_triangle_0():\n    assert test.triangle(0, 0, 0) == 'Equilateral triangle'\n    assert test.triangle(0, 0, 1) == 'Isosceles triangle'\n    assert test.triangle(2, 0, 1) == 'Scalene triangle'\n```\n\nSee the Klara\\'s documentation at \u003chttps://klara-py.readthedocs.io\u003e\n\n**Note**: Klara is still in early experimental stage, notable missing features are loop, comprehension, module import, exceptions and many more.\nSee [limitations](https://klara-py.readthedocs.io/en/latest/limitation.html) for full list. It probably will not run on real world projects, so it's best\nto cherry-pick a few interesting functions to generate the corresponding test case.\n\n## Installing\n\nKlara can be installed via `pip` tool by using:\n\n    pip install klara\n\n## Usage\n\nWe can invoke `klara` on any python source file, and it will generate a\ncorresponding pytest test file.\n\n``` shell\n$ cat source.py\ndef foo(x: int, y: int, z: str):\n    if x + y \u003e 2:\n        return x + y + 12\n    elif x \u003c y:\n        return x + y\n    elif (z + \"me\") == \"some\":\n        return z + \"thing\"\n    else:\n        return x - y\n\n$ klara source.py\n$ cat test_source.py\nimport contract_test\n\n\ndef test_foo_0():\n    assert contract_test.foo(0, 3, \\'\\') == 15\n    assert contract_test.foo(0, 1, \\'\\') == 1\n    assert contract_test.foo(0, 0, \\'so\\') == \\'sothing\\'\n    assert contract_test.foo(0, 0, \\'\\') == 0\n```\n\nConsult the [quick start](https://klara-py.readthedocs.io/en/latest/quick_start.html) manual for more examples and\nguidance. To use it as a static analysis library, go to\n[Inference](https://klara-py.readthedocs.io/en/latest/inference.html).\n\n## Why Klara?\n\nKlara works on ast level and it doesn\\'t execute user code in any way,\nwhich is a very important difference compared to similar tool like\n[Crosshair](https://github.com/pschanely/CrossHair) and\n[Pynguin](https://github.com/se2p/pynguin) that utilize concolic\nsymbolic execution that required user code execution that might cause\nunwanted side effects. Klara work on ast level, combine with data flow analysis\nthat utilize Control Flow Graph(CFG), Static Single Assignment(SSA), use-def chain, etc\\... to build a\npowerful python inference system that leverages Z3-solver for\nconstraints solving and path feasibility check. Because of this, Klara\nis able to operate on both python2/3 source code with the help of\n[typed_ast](https://github.com/python/typed_ast). To specify the source\ncode is in python 2, pass in `-py 2` argument. It\\'s python 3 by\ndefault.\n\nKlara can also be used as a static analysis tool, allow user to define\ncustom rule to identify programming bugs, error or enforcing coding\nstandard. With SMT solver support, analysis will be more accurate and\ngreatly reduce false-positive case. For example\n\n``` python\nimport klara\ntree = klara.parse(\"\"\"\n    def foo(v1: int):\n        if v1 \u003e 4:\n            if v1 \u003c 3:\n                z = 1\n            else:\n                z = 2\n        else:\n            z = 3\n        s = z\n\"\"\")\nwith klara.MANAGER.initialize_z3_var_from_func(tree.body[0]):\n    print(list(tree.body[0].body[-1].value.infer()))\n```\n\nWill print out:\n\n    [2, 3]\n\nBecause `z = 1` is not possible due to `v1 \u003e 4` and `v1 \u003c 3` is unsatisfiable\n\nThe inference system architecture and api is largely inspired by\n[Astroid](https://github.com/PyCQA/astroid), a static inference library\nused by [Pylint](https://github.com/PyCQA/pylint).\n\nKlara utilize the inference system to generate test case, in other\nwords, it **generate test case for all possible return values of the\nfunction**, instead of generate test case for all control path of the\nfunction.\n\nTo illustrate the point, consider the function below, with `divide by\nzero` vulnerabilities at line 3\n\n``` python\ndef foo(v1: int, v2: float):\n    if v1 \u003e 10000:\n        s = v1 / 0  # unused statement\n    if v1 \u003e v2:\n        s = v1\n    else:\n        s = v2\n    return s\n```\n\nKlara will generate test inputs below\n\n``` python\nimport contract_test\ndef test_foo_0():\n    assert contract_test.foo(0, -1.0) == 0\n    assert contract_test.foo(0, 0.0) == 0.0\n```\n\nIt doesn\\'t generate input `v1 \u003e 10000`, so the test case would not be\nable to find out the exceptions. This is because the `s` at\nline 3 is unused in the return value.\n\nIf we modify the second if statement to `elif`, which we\\'ll\nbe able to return the [s]{.title-ref} at line 3, klara will generate\ntest inputs that cover `v1 \u003e 10000` case.\n\nThis is an important distinction with other automatic test case\ngeneration available now, because by only generate test case for return\nvalues, we can generate a minimal test case, and it\\'s easier to\ncustomize how do Klara cover the function.\n\nFor example, say we are composing a complex system\n\n``` python\n    def main(number: int, cm: int, dc: int, wn: int):\n        mc = 0\n        if wn \u003e 2:\n            if number \u003e 2 and number \u003e 2 or number \u003e 2:\n                if number \u003e 0:\n                    if wn \u003e 2 or wn \u003e 2:\n                        mc = 2\n                    else:\n                        mc = 5\n                else:\n                    mc = 100\n        else:\n            mc = 1\n        nnn = number * cm\n        if cm \u003c= 4:\n            num_incr = 4\n        else:\n            num_incr = cm\n        n_num_incr = nnn / num_incr\n        nnn_left = dc * num_incr * (n_num_incr / 2 + n_num_incr % 2)\n        nnn_right = nnn - nnn_left\n        is_flag = nnn_right\n        if is_flag:\n            cell = Component(nnn_right, options=[mc])\n        else:\n            cell = Component(nnn_right)\n        return cell\n```\n\nIt isn\\'t immediately clear to us how many possible return values there\nare. But we can utilize Klara to generate inputs instantly, below is the\ngenerated test\n\n``` python\nimport contract_test\ndef test_main_0():\n    assert contract_test.main(2, 4, 1, 3) is not None\n    assert contract_test.main(2, 4, -1, 6) is not None\n    assert contract_test.main(2, 4, 1, 4) is not None\n    assert contract_test.main(-2, 4, 3, 4) is not None\n    assert contract_test.main(-1, -1, -1, 2) is not None\n    assert contract_test.main(0, 0, 0, 3) is not None\n    assert contract_test.main(0, 0, 0, 6) is not None\n    assert contract_test.main(0, 0, 0, 4) is not None\n    assert contract_test.main(-2, 0, 0, 4) is not None\n    assert contract_test.main(0, 0, 0, 0) is not None\n```\n\nAbove generated 10 total results, which is product of\n`nnn_right` which have 2 possibilities and `mc` which have 5 possibilities.\n\nSuppose that 10 tests input is too much, and we have determine that the\n`options` argument to `Component` is redundant to test, we\ncan use Klara\\'s custom plugin to selectively determine which part to\nignore in test generation. Go to [customize coverage\nstrategy](https://klara-py.readthedocs.io/en/latest/customize_coverage_strategy.html) for more information.\n\nAfter we have setup the plugin, Klara will generate following test\n\n``` python\nimport contract_test\ndef test_main_0():\n    assert contract_test.main(1, 3, 0, 0) is not None\n    assert contract_test.main(0, 0, 0, 0) is not None\n```\n\nWhich is only 2 combinations of `nnn_right`\n\nBecause Klara can't dynamically execute the code, it will provide extension to specify how to infer \nspecific ast node or user defined type to make Klara \\'smarter\\'. It\\'s described in\n[extending](https://klara-py.readthedocs.io/en/latest/extending.html), [extending user\ntype](https://klara-py.readthedocs.io/en/latest/extending_user_type.html) and [customize coverage\nstrategy](https://klara-py.readthedocs.io/en/latest/customize_coverage_strategy.html).\n\n## Contributing\n\nWe use [Poetry](https://python-poetry.org/docs/) to manage dependencies.\nAfter poetry is installed, run:\n\n    $ poetry shell\n    $ poetry install\n\nTo run the test case, do:\n\n    $ poetry run pytest test\n\n## Acknowledgements\n- The architecture of the inference system is largely inspired by [Astroid](https://github.com/PyCQA/astroid).\n- Special thanks to Dr. Poh for guiding the early stages of the project.\n\n\n## License\n\nThis project is licensed under the terms of the GNU Lesser General\nPublic License.\n","funding_links":[],"categories":["Python","Tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusagitoneko97%2Fklara","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusagitoneko97%2Fklara","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusagitoneko97%2Fklara/lists"}