diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 0ec4934..95df148 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -7,19 +7,19 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: psf/black@stable check-doc: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: 3.8 - name: Sphinx @@ -32,22 +32,22 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [windows-latest, macos-latest, ubuntu-20.04] - python_version: [3.5, 3.6, 3.7, 3.8] - architecture: [x86, x64] + os: [windows-latest, macos-13, ubuntu-20.04] + python_version: [3.8] + architecture: [x64] exclude: - - os: macos-latest + - os: macos-13 architecture: x86 - os: ubuntu-20.04 architecture: x86 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python_version }} architecture: ${{ matrix.architecture }} @@ -70,14 +70,16 @@ jobs: runs-on: ubuntu-latest needs: [check, check-doc, test] if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') + permissions: + id-token: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: 3.8 - run: pip --disable-pip-version-check install wheel @@ -85,7 +87,4 @@ jobs: - name: Build packages run: python setup.py sdist bdist_wheel - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/CppHeaderParser/CppHeaderParser.py b/CppHeaderParser/CppHeaderParser.py index ad78c1e..c6061e6 100644 --- a/CppHeaderParser/CppHeaderParser.py +++ b/CppHeaderParser/CppHeaderParser.py @@ -141,6 +141,7 @@ def trace_print(*args): _BRACE_REASON_OTHER = 0 _BRACE_REASON_NS = 1 _BRACE_REASON_EXTERN = 2 +_BRACE_REASON_VARIABLE = 3 # Track what was added in what order and at what depth parseHistory = [] @@ -239,10 +240,13 @@ def is_property_namestack(nameStack): r = False if "(" not in nameStack and ")" not in nameStack: r = True - elif ( - "(" in nameStack - and "=" in nameStack - and nameStack.index("=") < nameStack.index("(") + elif "(" in nameStack and ( + ( # = initialization + "=" in nameStack and nameStack.index("=") < nameStack.index("(") + ) + or ( # {} initialization + "{" in nameStack and nameStack.index("{") < nameStack.index("(") + ) ): r = True # See if we are a function pointer @@ -1217,29 +1221,48 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): else: self["extern"] = False + if "=" in nameStack: + self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) + self["name"] = nameStack[nameStack.index("=") - 1] + default = " ".join(nameStack[nameStack.index("=") + 1 :]) + nameStack = nameStack[: nameStack.index("=")] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + elif "{" in nameStack and "}" in nameStack: + posBracket = nameStack.index("{") + self["type"] = " ".join(nameStack[: posBracket - 1]) + self["name"] = nameStack[posBracket - 1] + default = " ".join(nameStack[posBracket + 1 : -1]) + nameStack = nameStack[:posBracket] + default = self._filter_name(default) + self["default"] = default + # backwards compat; deprecate camelCase in dicts + self["defaultValue"] = default + _stack_ = nameStack - if "[" in nameStack: # strip off array informatin - arrayStack = nameStack[nameStack.index("[") :] - if nameStack.count("[") > 1: + self["array"] = 0 + while "]" in nameStack[-1]: # strip off array information + arrayPos = len(nameStack) - 1 - nameStack[::-1].index("[") + arrayStack = nameStack[arrayPos:] + if self["array"] == 1: debug_print("Multi dimensional array") debug_print("arrayStack=%s", arrayStack) - nums = [x for x in arrayStack if x.isdigit()] - # Calculate size by multiplying all dimensions - p = 1 - for n in nums: - p *= int(n) + if len(arrayStack) == 3: + n = arrayStack[1] # Multi dimensional array - self["array_size"] = p + if not "multi_dimensional_array_size" in self: + self["multi_dimensional_array_size"] = self["array_size"] + self["multi_dimensional_array_size"] += "x" + n + self["array_size"] = str(int(self["array_size"]) * int(n)) self["multi_dimensional_array"] = 1 - self["multi_dimensional_array_size"] = "x".join(nums) else: debug_print("Array") if len(arrayStack) == 3: self["array_size"] = arrayStack[1] - nameStack = nameStack[: nameStack.index("[")] + nameStack = nameStack[:arrayPos] self["array"] = 1 - else: - self["array"] = 0 nameStack = self._name_stack_helper(nameStack) if doxygen: @@ -1268,15 +1291,6 @@ def __init__(self, nameStack, doxygen, location, is_var=True, **kwargs): ) self["function_pointer"] = 1 - elif "=" in nameStack: - self["type"] = " ".join(nameStack[: nameStack.index("=") - 1]) - self["name"] = nameStack[nameStack.index("=") - 1] - default = " ".join(nameStack[nameStack.index("=") + 1 :]) - default = self._filter_name(default) - self["default"] = default - # backwards compat; deprecate camelCase in dicts - self["defaultValue"] = default - elif ( is_fundamental(nameStack[-1]) or nameStack[-1] in [">", "<", ":", "."] @@ -1879,9 +1893,9 @@ def finalize_vars(self): elif tag in self._template_typenames: var["typename"] = tag var["ctypes_type"] = "ctypes.c_void_p" - var[ - "unresolved" - ] = True # TODO, how to deal with templates? + var["unresolved"] = ( + True # TODO, how to deal with templates? + ) elif tag.startswith( "_" @@ -2931,7 +2945,23 @@ def __init__( continue if parenDepth == 0 and tok.type == "{": - self.lastBraceReason = _BRACE_REASON_OTHER + if self.nameStack[0] in ( + "class", + "struct", + "union", + "namespace", + "enum", + "extern", + "typedef", + ) or (is_method_namestack(self.stack) or (not self.curClass)): + self.lastBraceReason = _BRACE_REASON_OTHER + else: + # Case : type variable {init}; + self.lastBraceReason = _BRACE_REASON_VARIABLE + self.braceDepth += 1 + self.braceReason.append(self.lastBraceReason) + self.nameStack.append(tok.value) + continue if len(self.nameStack) >= 2 and is_namespace( self.nameStack ): # namespace {} with no name used in boost, this sets default? @@ -2991,6 +3021,10 @@ def __init__( self.linkage_stack.pop() self.stack = [] # clear stack when linkage ends? self.stmtTokens = [] + # Case : type variable {init}; + elif reason == _BRACE_REASON_VARIABLE: + self.nameStack.append(tok.value) + continue else: self._evaluate_stack() self.braceDepth -= 1 diff --git a/test/test_CppHeaderParser.py b/test/test_CppHeaderParser.py index a7810a7..4f949f5 100644 --- a/test/test_CppHeaderParser.py +++ b/test/test_CppHeaderParser.py @@ -1162,14 +1162,9 @@ def test_method_params(self): [], ) - def test_class_template(self): - self.assertEqual( - self.cppHeader.classes["CarrotClass"]["template"], "template" - ) - # Bug 3517289 -class CarrotClass_TestCase(unittest.TestCase): +class ExternClass_TestCase(unittest.TestCase): def setUp(self): self.cppHeader = CppHeaderParser.CppHeader("TestSampleClass.h") @@ -2364,7 +2359,7 @@ def setUp(self): def test_array_size(self): self.assertEqual( self.cppHeader.classes["Picture"]["properties"]["public"][1]["array_size"], - 16384, + "16384", ) def test_multi_dimensional_array_size(self): @@ -4227,5 +4222,88 @@ def test_fn(self): self.assertEqual(fn["linkage"], "") +# Github PR 85 +class ContainerOfArray_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class ContainerOfArray { +public: + std::unique_ptr variable; + std::unique_ptr function(std::unique_ptr param1); +}; +""", + "string", + ) + + def test_rtntype(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "rtnType" + ], + "std::unique_ptr", + ) + + def test_parameters(self): + self.assertEqual( + filter_pameters( + self.cppHeader.classes["ContainerOfArray"]["methods"]["public"][0][ + "parameters" + ] + ), + [{"name": "param1", "desc": None, "type": "std::unique_ptr"}], + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "name" + ], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["ContainerOfArray"]["properties"]["public"][0][ + "type" + ], + "std::unique_ptr", + ) + + +class InitBracket_TestCase(unittest.TestCase): + def setUp(self): + self.cppHeader = CppHeaderParser.CppHeader( + """ +class InitBracket { +public: + int variable{10}; + std::shared_ptr variable2 {std::make_shared(150)}; +}; +""", + "string", + ) + + def test_member(self): + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0]["name"], + "variable", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][0][ + "defaultValue" + ], + "10", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1]["name"], + "variable2", + ) + self.assertEqual( + self.cppHeader.classes["InitBracket"]["properties"]["public"][1][ + "defaultValue" + ], + "std::make_shared ( 150 )", + ) + + if __name__ == "__main__": unittest.main()