From dcc507542b0328d9452b85f221a739aec85367ad Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Thu, 13 Feb 2025 18:03:50 +0100 Subject: [PATCH 1/2] When saving, exclude_unset doesn't always behave as expected So we remove exclude_unset, which will increase the size of the output, but that's not big deal. --- CHANGES.txt | 2 +- src/pyocf/captable.py | 6 +++--- tests/test_save.py | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 641d34c..b3e4c62 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,7 +4,7 @@ Changes 1.2.0b3 (unreleased) -------------------- -- Nothing changed yet. +- Now includes default values when saving, making the OCF files larger. 1.2.0b2 (2025-02-04) diff --git a/src/pyocf/captable.py b/src/pyocf/captable.py index 096830d..b89d2bf 100644 --- a/src/pyocf/captable.py +++ b/src/pyocf/captable.py @@ -103,7 +103,7 @@ def file_factory(p): def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty): if issuer is None and self.manifest is None: raise ValueError( - "You must specify an issuer, either by passing the value to the" + "You must specify an issuer, either by passing the value to the " "save method, or by creating a Manifest." ) @@ -125,7 +125,7 @@ def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty): with file_factory(ocffilename) as ocffile: itemfile = fileob(items=getattr(self, filetype)) - jsonstr = itemfile.json(exclude_unset=True) + jsonstr = itemfile.model_dump_json() if pretty: jsonstr = json.dumps(json.loads(jsonstr), indent=4) jsonstr = jsonstr.encode("UTF-8") @@ -156,7 +156,7 @@ def _save_ocf_files(self, manifest_path, issuer, file_factory, pretty): self.manifest = ocfmanifestfile.OCFManifestFile(**manifest_data) with file_factory(manifest_path) as ocffile: - jsonstr = self.manifest.json() + jsonstr = self.manifest.model_dump_json() if pretty: jsonstr = json.dumps(json.loads(jsonstr), indent=4) ocffile.write(jsonstr.encode("UTF-8")) diff --git a/tests/test_save.py b/tests/test_save.py index 1148781..fbf2eed 100644 --- a/tests/test_save.py +++ b/tests/test_save.py @@ -57,6 +57,11 @@ def test_save_directory(): # Do some simple checks on the data assert b'"file_type": "OCF_MANIFEST_FILE"' in data assert b'"filepath": "StockLegends.ocf.json",' in data + + transactions = [x for x in files if x.name == "Transactions.ocf.json"][0] + data = transactions.open("rb").read() + assert b'"object_type": "TX_EQUITY_COMPENSATION_RELEASE"' in data + except (PermissionError, NotADirectoryError): # Bugs in windows give permission errors for absolutely no reason # when deleting temporary files. Ignore them. From 7995f65ead7de39ab6ea6847b02aac6ab49ac0bd Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Thu, 17 Apr 2025 12:04:54 +0200 Subject: [PATCH 2/2] Make it possible to pass in data into the Captable constructor --- CHANGES.txt | 3 +++ docs/source/using.rst | 22 +++++++++++++++------- src/pyocf/captable.py | 44 +++++++++++++++++++++++++++++++++---------- tests/test_basic.py | 13 +++++++++++++ 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b3e4c62..63fc8f5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,9 @@ Changes - Now includes default values when saving, making the OCF files larger. +- It's now possible to pass in the data to the Captable object when + creating it, instead of adding the data to the attribute lists later. + 1.2.0b2 (2025-02-04) -------------------- diff --git a/docs/source/using.rst b/docs/source/using.rst index 345efd9..ef30c04 100644 --- a/docs/source/using.rst +++ b/docs/source/using.rst @@ -46,7 +46,6 @@ but you then need to create ``File`` objects for each file, with dummy md5 hashe ... id="d6c49a5a-257d-4b41-9f1d-073a77dfe719", ... name={"legal_name": "Person Y"}, ... stakeholder_type="INDIVIDUAL", - ... comments=[], ... ) ... ) @@ -69,6 +68,20 @@ but you then need to create ``File`` objects for each file, with dummy md5 hashe ... ) ... ) +It is also possible to create the list of objects first, and pass them in to the +Captable constructor:: + + >>> sh_list = [ + ... api.Stakeholder( + ... object_type="STAKEHOLDER", + ... id="'917efd77a370-d1f9-14b4-d752-a5a94c6d", + ... name={"legal_name": "Person X"}, + ... stakeholder_type="INDIVIDUAL", + ... ) + ... } + + >>> cap2 = Captable(stakeholders=sh_list) + And once you have filled in all the lists with all the information, you save the captable: @@ -102,12 +115,7 @@ A captable will then be created and Python objects will be stored in it. 'pyocf example docs' >>> cap.stakeholders # doctest: +NORMALIZE_WHITESPACE - [Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=[], - object_type='STAKEHOLDER', name=Name(legal_name='Person Y', first_name=None, - last_name=None), stakeholder_type=, issuer_assigned_id=None, current_relationship=None, - primary_contact=None, contact_info=None, addresses=None, tax_ids=None), - Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=[], + [Stakeholder(id='d6c49a5a-257d-4b41-9f1d-073a77dfe719', comments=None, object_type='STAKEHOLDER', name=Name(legal_name='Person Y', first_name=None, last_name=None), stakeholder_type=, issuer_assigned_id=None, current_relationship=None, diff --git a/src/pyocf/captable.py b/src/pyocf/captable.py index b89d2bf..bf3fde2 100644 --- a/src/pyocf/captable.py +++ b/src/pyocf/captable.py @@ -43,16 +43,40 @@ class Captable: - manifest: ocfmanifestfile.OCFManifestFile = None - documents: list[Document] = [] - financings: list[Financing] = [] - stakeholders: list[Stakeholder] = [] - stock_classes: list[StockClass] = [] - stock_legend_templates: list[StockLegendTemplate] = [] - stock_plans: list[StockPlan] = [] - transactions: list[Transaction] = [] - valuations: list[Valuation] = [] - vesting_terms: list[VestingTerms] = [] + manifest: ocfmanifestfile.OCFManifestFile + documents: list[Document] + financings: list[Financing] + stakeholders: list[Stakeholder] + stock_classes: list[StockClass] + stock_legend_templates: list[StockLegendTemplate] + stock_plans: list[StockPlan] + transactions: list[Transaction] + valuations: list[Valuation] + vesting_terms: list[VestingTerms] + + def __init__( + self, + manifest: ocfmanifestfile.OCFManifestFile = None, + documents: list[Document] = None, + financings: list[Financing] = None, + stakeholders: list[Stakeholder] = None, + stock_classes: list[StockClass] = None, + stock_legend_templates: list[StockLegendTemplate] = None, + stock_plans: list[StockPlan] = None, + transactions: list[Transaction] = None, + valuations: list[Valuation] = None, + vesting_terms: list[VestingTerms] = None, + ): + self.manifest = manifest + self.documents = documents or [] + self.financings = financings or [] + self.stakeholders = stakeholders or [] + self.stock_classes = stock_classes or [] + self.stock_legend_templates = stock_legend_templates or [] + self.stock_plans = stock_plans or [] + self.transactions = transactions or [] + self.valuations = valuations or [] + self.vesting_terms = vesting_terms or [] @classmethod def load(cls, location): diff --git a/tests/test_basic.py b/tests/test_basic.py index 8e336a5..d824370 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -7,6 +7,7 @@ from pyocf.api import StakeholdersFile from pyocf.api import Stakeholder from pyocf.api import Name, Phone, CountryCode +from pyocf.captable import Captable def test_basic_loading(): @@ -79,3 +80,15 @@ def test_constrained_strings(): with pytest.raises(ValueError): CountryCode("SPUT") + + +def test_captable_init(): + stakeholders = [ + Stakeholder( + id="steve", + name=Name(legal_name="Stake Holder"), + stakeholder_type=StakeholderType.ENUM_INDIVIDUAL, + ) + ] + captable = Captable(stakeholders=stakeholders) + assert captable.stakeholders[0].name.legal_name == "Stake Holder"