From b85e4b14f4dbcb6a60cce385ce922c75af15e1be Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Fri, 29 Nov 2024 11:48:58 +0800 Subject: [PATCH 1/3] Add support for generating bindings for token contracts. --- stellar_contract_bindings/metadata.py | 15 +++ stellar_contract_bindings/python.py | 84 +++++++++--- stellar_contract_bindings/utils.py | 37 +++++- tests/client.py | 139 ++++++++++++++++++++ tests/contracts/contracts/python/src/lib.rs | 45 ++++++- tests/test_client.py | 2 +- 6 files changed, 297 insertions(+), 25 deletions(-) diff --git a/stellar_contract_bindings/metadata.py b/stellar_contract_bindings/metadata.py index 0d8889b..c0ba038 100644 --- a/stellar_contract_bindings/metadata.py +++ b/stellar_contract_bindings/metadata.py @@ -2,6 +2,7 @@ import dataclasses from typing import List, Optional, Tuple, Type, Union +import xdrlib3 from stellar_sdk import xdr @@ -108,3 +109,17 @@ def parse_entries( offset += len(entry.to_xdr_bytes()) entries.append(entry) return entries + + +def get_token_sc_spec_entry() -> list[xdr.SCSpecEntry]: + """Get the token contract spec entry.""" + + # A little bit hacky, but it works + # TODO: find a way to get the token contract spec entry in the repo + # https://github.com/stellar/stellar-cli/blob/a11a924d310c1602e7b579377daa3e373010ac0e/cmd/soroban-cli/src/get_spec.rs#L77 + raw_xdr = "AAAAAAAAAYpSZXR1cm5zIHRoZSBhbGxvd2FuY2UgZm9yIGBzcGVuZGVyYCB0byB0cmFuc2ZlciBmcm9tIGBmcm9tYC4KClRoZSBhbW91bnQgcmV0dXJuZWQgaXMgdGhlIGFtb3VudCB0aGF0IHNwZW5kZXIgaXMgYWxsb3dlZCB0byB0cmFuc2ZlcgpvdXQgb2YgZnJvbSdzIGJhbGFuY2UuIFdoZW4gdGhlIHNwZW5kZXIgdHJhbnNmZXJzIGFtb3VudHMsIHRoZSBhbGxvd2FuY2UKd2lsbCBiZSByZWR1Y2VkIGJ5IHRoZSBhbW91bnQgdHJhbnNmZXJyZWQuCgojIEFyZ3VtZW50cwoKKiBgZnJvbWAgLSBUaGUgYWRkcmVzcyBob2xkaW5nIHRoZSBiYWxhbmNlIG9mIHRva2VucyB0byBiZSBkcmF3biBmcm9tLgoqIGBzcGVuZGVyYCAtIFRoZSBhZGRyZXNzIHNwZW5kaW5nIHRoZSB0b2tlbnMgaGVsZCBieSBgZnJvbWAuAAAAAAAJYWxsb3dhbmNlAAAAAAAAAgAAAAAAAAAEZnJvbQAAABMAAAAAAAAAB3NwZW5kZXIAAAAAEwAAAAEAAAALAAAAAAAAAIlSZXR1cm5zIHRydWUgaWYgYGlkYCBpcyBhdXRob3JpemVkIHRvIHVzZSBpdHMgYmFsYW5jZS4KCiMgQXJndW1lbnRzCgoqIGBpZGAgLSBUaGUgYWRkcmVzcyBmb3Igd2hpY2ggdG9rZW4gYXV0aG9yaXphdGlvbiBpcyBiZWluZyBjaGVja2VkLgAAAAAAAAphdXRob3JpemVkAAAAAAABAAAAAAAAAAJpZAAAAAAAEwAAAAEAAAABAAAAAAAAA59TZXQgdGhlIGFsbG93YW5jZSBieSBgYW1vdW50YCBmb3IgYHNwZW5kZXJgIHRvIHRyYW5zZmVyL2J1cm4gZnJvbQpgZnJvbWAuCgpUaGUgYW1vdW50IHNldCBpcyB0aGUgYW1vdW50IHRoYXQgc3BlbmRlciBpcyBhcHByb3ZlZCB0byB0cmFuc2ZlciBvdXQgb2YKZnJvbSdzIGJhbGFuY2UuIFRoZSBzcGVuZGVyIHdpbGwgYmUgYWxsb3dlZCB0byB0cmFuc2ZlciBhbW91bnRzLCBhbmQKd2hlbiBhbiBhbW91bnQgaXMgdHJhbnNmZXJyZWQgdGhlIGFsbG93YW5jZSB3aWxsIGJlIHJlZHVjZWQgYnkgdGhlCmFtb3VudCB0cmFuc2ZlcnJlZC4KCiMgQXJndW1lbnRzCgoqIGBmcm9tYCAtIFRoZSBhZGRyZXNzIGhvbGRpbmcgdGhlIGJhbGFuY2Ugb2YgdG9rZW5zIHRvIGJlIGRyYXduIGZyb20uCiogYHNwZW5kZXJgIC0gVGhlIGFkZHJlc3MgYmVpbmcgYXV0aG9yaXplZCB0byBzcGVuZCB0aGUgdG9rZW5zIGhlbGQgYnkKYGZyb21gLgoqIGBhbW91bnRgIC0gVGhlIHRva2VucyB0byBiZSBtYWRlIGF2YWlsYWJsZSB0byBgc3BlbmRlcmAuCiogYGV4cGlyYXRpb25fbGVkZ2VyYCAtIFRoZSBsZWRnZXIgbnVtYmVyIHdoZXJlIHRoaXMgYWxsb3dhbmNlIGV4cGlyZXMuIENhbm5vdApiZSBsZXNzIHRoYW4gdGhlIGN1cnJlbnQgbGVkZ2VyIG51bWJlciB1bmxlc3MgdGhlIGFtb3VudCBpcyBiZWluZyBzZXQgdG8gMC4KQW4gZXhwaXJlZCBlbnRyeSAod2hlcmUgZXhwaXJhdGlvbl9sZWRnZXIgPCB0aGUgY3VycmVudCBsZWRnZXIgbnVtYmVyKQpzaG91bGQgYmUgdHJlYXRlZCBhcyBhIDAgYW1vdW50IGFsbG93YW5jZS4KCiMgRXZlbnRzCgpFbWl0cyBhbiBldmVudCB3aXRoIHRvcGljcyBgWyJhcHByb3ZlIiwgZnJvbTogQWRkcmVzcywKc3BlbmRlcjogQWRkcmVzc10sIGRhdGEgPSBbYW1vdW50OiBpMTI4LCBleHBpcmF0aW9uX2xlZGdlcjogdTMyXWAAAAAAB2FwcHJvdmUAAAAABAAAAAAAAAAEZnJvbQAAABMAAAAAAAAAB3NwZW5kZXIAAAAAEwAAAAAAAAAGYW1vdW50AAAAAAALAAAAAAAAABFleHBpcmF0aW9uX2xlZGdlcgAAAAAAAAQAAAAAAAAAAAAAAJhSZXR1cm5zIHRoZSBiYWxhbmNlIG9mIGBpZGAuCgojIEFyZ3VtZW50cwoKKiBgaWRgIC0gVGhlIGFkZHJlc3MgZm9yIHdoaWNoIGEgYmFsYW5jZSBpcyBiZWluZyBxdWVyaWVkLiBJZiB0aGUKYWRkcmVzcyBoYXMgbm8gZXhpc3RpbmcgYmFsYW5jZSwgcmV0dXJucyAwLgAAAAdiYWxhbmNlAAAAAAEAAAAAAAAAAmlkAAAAAAATAAAAAQAAAAsAAAAAAAABYkJ1cm4gYGFtb3VudGAgZnJvbSBgZnJvbWAuCgpSZWR1Y2VzIGZyb20ncyBiYWxhbmNlIGJ5IHRoZSBhbW91bnQsIHdpdGhvdXQgdHJhbnNmZXJyaW5nIHRoZSBiYWxhbmNlCnRvIGFub3RoZXIgaG9sZGVyJ3MgYmFsYW5jZS4KCiMgQXJndW1lbnRzCgoqIGBmcm9tYCAtIFRoZSBhZGRyZXNzIGhvbGRpbmcgdGhlIGJhbGFuY2Ugb2YgdG9rZW5zIHdoaWNoIHdpbGwgYmUKYnVybmVkIGZyb20uCiogYGFtb3VudGAgLSBUaGUgYW1vdW50IG9mIHRva2VucyB0byBiZSBidXJuZWQuCgojIEV2ZW50cwoKRW1pdHMgYW4gZXZlbnQgd2l0aCB0b3BpY3MgYFsiYnVybiIsIGZyb206IEFkZHJlc3NdLCBkYXRhID0gYW1vdW50OgppMTI4YAAAAAAABGJ1cm4AAAACAAAAAAAAAARmcm9tAAAAEwAAAAAAAAAGYW1vdW50AAAAAAALAAAAAAAAAAAAAALaQnVybiBgYW1vdW50YCBmcm9tIGBmcm9tYCwgY29uc3VtaW5nIHRoZSBhbGxvd2FuY2Ugb2YgYHNwZW5kZXJgLgoKUmVkdWNlcyBmcm9tJ3MgYmFsYW5jZSBieSB0aGUgYW1vdW50LCB3aXRob3V0IHRyYW5zZmVycmluZyB0aGUgYmFsYW5jZQp0byBhbm90aGVyIGhvbGRlcidzIGJhbGFuY2UuCgpUaGUgc3BlbmRlciB3aWxsIGJlIGFsbG93ZWQgdG8gYnVybiB0aGUgYW1vdW50IGZyb20gZnJvbSdzIGJhbGFuY2UsIGlmCnRoZSBhbW91bnQgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRoZSBhbGxvd2FuY2UgdGhhdCB0aGUgc3BlbmRlciBoYXMKb24gdGhlIGZyb20ncyBiYWxhbmNlLiBUaGUgc3BlbmRlcidzIGFsbG93YW5jZSBvbiBmcm9tJ3MgYmFsYW5jZSB3aWxsIGJlCnJlZHVjZWQgYnkgdGhlIGFtb3VudC4KCiMgQXJndW1lbnRzCgoqIGBzcGVuZGVyYCAtIFRoZSBhZGRyZXNzIGF1dGhvcml6aW5nIHRoZSBidXJuLCBhbmQgaGF2aW5nIGl0cyBhbGxvd2FuY2UKY29uc3VtZWQgZHVyaW5nIHRoZSBidXJuLgoqIGBmcm9tYCAtIFRoZSBhZGRyZXNzIGhvbGRpbmcgdGhlIGJhbGFuY2Ugb2YgdG9rZW5zIHdoaWNoIHdpbGwgYmUKYnVybmVkIGZyb20uCiogYGFtb3VudGAgLSBUaGUgYW1vdW50IG9mIHRva2VucyB0byBiZSBidXJuZWQuCgojIEV2ZW50cwoKRW1pdHMgYW4gZXZlbnQgd2l0aCB0b3BpY3MgYFsiYnVybiIsIGZyb206IEFkZHJlc3NdLCBkYXRhID0gYW1vdW50OgppMTI4YAAAAAAACWJ1cm5fZnJvbQAAAAAAAAMAAAAAAAAAB3NwZW5kZXIAAAAAEwAAAAAAAAAEZnJvbQAAABMAAAAAAAAABmFtb3VudAAAAAAACwAAAAAAAAAAAAABUUNsYXdiYWNrIGBhbW91bnRgIGZyb20gYGZyb21gIGFjY291bnQuIGBhbW91bnRgIGlzIGJ1cm5lZCBpbiB0aGUKY2xhd2JhY2sgcHJvY2Vzcy4KCiMgQXJndW1lbnRzCgoqIGBmcm9tYCAtIFRoZSBhZGRyZXNzIGhvbGRpbmcgdGhlIGJhbGFuY2UgZnJvbSB3aGljaCB0aGUgY2xhd2JhY2sgd2lsbAp0YWtlIHRva2Vucy4KKiBgYW1vdW50YCAtIFRoZSBhbW91bnQgb2YgdG9rZW5zIHRvIGJlIGNsYXdlZCBiYWNrLgoKIyBFdmVudHMKCkVtaXRzIGFuIGV2ZW50IHdpdGggdG9waWNzIGBbImNsYXdiYWNrIiwgYWRtaW46IEFkZHJlc3MsIHRvOiBBZGRyZXNzXSwKZGF0YSA9IGFtb3VudDogaTEyOGAAAAAAAAAIY2xhd2JhY2sAAAACAAAAAAAAAARmcm9tAAAAEwAAAAAAAAAGYW1vdW50AAAAAAALAAAAAAAAAAAAAACAUmV0dXJucyB0aGUgbnVtYmVyIG9mIGRlY2ltYWxzIHVzZWQgdG8gcmVwcmVzZW50IGFtb3VudHMgb2YgdGhpcyB0b2tlbi4KCiMgUGFuaWNzCgpJZiB0aGUgY29udHJhY3QgaGFzIG5vdCB5ZXQgYmVlbiBpbml0aWFsaXplZC4AAAAIZGVjaW1hbHMAAAAAAAAAAQAAAAQAAAAAAAAA801pbnRzIGBhbW91bnRgIHRvIGB0b2AuCgojIEFyZ3VtZW50cwoKKiBgdG9gIC0gVGhlIGFkZHJlc3Mgd2hpY2ggd2lsbCByZWNlaXZlIHRoZSBtaW50ZWQgdG9rZW5zLgoqIGBhbW91bnRgIC0gVGhlIGFtb3VudCBvZiB0b2tlbnMgdG8gYmUgbWludGVkLgoKIyBFdmVudHMKCkVtaXRzIGFuIGV2ZW50IHdpdGggdG9waWNzIGBbIm1pbnQiLCBhZG1pbjogQWRkcmVzcywgdG86IEFkZHJlc3NdLCBkYXRhCj0gYW1vdW50OiBpMTI4YAAAAAAEbWludAAAAAIAAAAAAAAAAnRvAAAAAAATAAAAAAAAAAZhbW91bnQAAAAAAAsAAAAAAAAAAAAAAFlSZXR1cm5zIHRoZSBuYW1lIGZvciB0aGlzIHRva2VuLgoKIyBQYW5pY3MKCklmIHRoZSBjb250cmFjdCBoYXMgbm90IHlldCBiZWVuIGluaXRpYWxpemVkLgAAAAAAAARuYW1lAAAAAAAAAAEAAAAQAAAAAAAAAQxTZXRzIHRoZSBhZG1pbmlzdHJhdG9yIHRvIHRoZSBzcGVjaWZpZWQgYWRkcmVzcyBgbmV3X2FkbWluYC4KCiMgQXJndW1lbnRzCgoqIGBuZXdfYWRtaW5gIC0gVGhlIGFkZHJlc3Mgd2hpY2ggd2lsbCBoZW5jZWZvcnRoIGJlIHRoZSBhZG1pbmlzdHJhdG9yCm9mIHRoaXMgdG9rZW4gY29udHJhY3QuCgojIEV2ZW50cwoKRW1pdHMgYW4gZXZlbnQgd2l0aCB0b3BpY3MgYFsic2V0X2FkbWluIiwgYWRtaW46IEFkZHJlc3NdLCBkYXRhID0KW25ld19hZG1pbjogQWRkcmVzc11gAAAACXNldF9hZG1pbgAAAAAAAAEAAAAAAAAACW5ld19hZG1pbgAAAAAAABMAAAAAAAAAAAAAAEZSZXR1cm5zIHRoZSBhZG1pbiBvZiB0aGUgY29udHJhY3QuCgojIFBhbmljcwoKSWYgdGhlIGFkbWluIGlzIG5vdCBzZXQuAAAAAAAFYWRtaW4AAAAAAAAAAAAAAQAAABMAAAAAAAABUFNldHMgd2hldGhlciB0aGUgYWNjb3VudCBpcyBhdXRob3JpemVkIHRvIHVzZSBpdHMgYmFsYW5jZS4gSWYKYGF1dGhvcml6ZWRgIGlzIHRydWUsIGBpZGAgc2hvdWxkIGJlIGFibGUgdG8gdXNlIGl0cyBiYWxhbmNlLgoKIyBBcmd1bWVudHMKCiogYGlkYCAtIFRoZSBhZGRyZXNzIGJlaW5nIChkZS0pYXV0aG9yaXplZC4KKiBgYXV0aG9yaXplYCAtIFdoZXRoZXIgb3Igbm90IGBpZGAgY2FuIHVzZSBpdHMgYmFsYW5jZS4KCiMgRXZlbnRzCgpFbWl0cyBhbiBldmVudCB3aXRoIHRvcGljcyBgWyJzZXRfYXV0aG9yaXplZCIsIGlkOiBBZGRyZXNzXSwgZGF0YSA9ClthdXRob3JpemU6IGJvb2xdYAAAAA5zZXRfYXV0aG9yaXplZAAAAAAAAgAAAAAAAAACaWQAAAAAABMAAAAAAAAACWF1dGhvcml6ZQAAAAAAAAEAAAAAAAAAAAAAAFtSZXR1cm5zIHRoZSBzeW1ib2wgZm9yIHRoaXMgdG9rZW4uCgojIFBhbmljcwoKSWYgdGhlIGNvbnRyYWN0IGhhcyBub3QgeWV0IGJlZW4gaW5pdGlhbGl6ZWQuAAAAAAZzeW1ib2wAAAAAAAAAAAABAAAAEAAAAAAAAAFiVHJhbnNmZXIgYGFtb3VudGAgZnJvbSBgZnJvbWAgdG8gYHRvYC4KCiMgQXJndW1lbnRzCgoqIGBmcm9tYCAtIFRoZSBhZGRyZXNzIGhvbGRpbmcgdGhlIGJhbGFuY2Ugb2YgdG9rZW5zIHdoaWNoIHdpbGwgYmUKd2l0aGRyYXduIGZyb20uCiogYHRvYCAtIFRoZSBhZGRyZXNzIHdoaWNoIHdpbGwgcmVjZWl2ZSB0aGUgdHJhbnNmZXJyZWQgdG9rZW5zLgoqIGBhbW91bnRgIC0gVGhlIGFtb3VudCBvZiB0b2tlbnMgdG8gYmUgdHJhbnNmZXJyZWQuCgojIEV2ZW50cwoKRW1pdHMgYW4gZXZlbnQgd2l0aCB0b3BpY3MgYFsidHJhbnNmZXIiLCBmcm9tOiBBZGRyZXNzLCB0bzogQWRkcmVzc10sCmRhdGEgPSBhbW91bnQ6IGkxMjhgAAAAAAAIdHJhbnNmZXIAAAADAAAAAAAAAARmcm9tAAAAEwAAAAAAAAACdG8AAAAAABMAAAAAAAAABmFtb3VudAAAAAAACwAAAAAAAAAAAAADMVRyYW5zZmVyIGBhbW91bnRgIGZyb20gYGZyb21gIHRvIGB0b2AsIGNvbnN1bWluZyB0aGUgYWxsb3dhbmNlIHRoYXQKYHNwZW5kZXJgIGhhcyBvbiBgZnJvbWAncyBiYWxhbmNlLiBBdXRob3JpemVkIGJ5IHNwZW5kZXIKKGBzcGVuZGVyLnJlcXVpcmVfYXV0aCgpYCkuCgpUaGUgc3BlbmRlciB3aWxsIGJlIGFsbG93ZWQgdG8gdHJhbnNmZXIgdGhlIGFtb3VudCBmcm9tIGZyb20ncyBiYWxhbmNlCmlmIHRoZSBhbW91bnQgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHRoZSBhbGxvd2FuY2UgdGhhdCB0aGUgc3BlbmRlcgpoYXMgb24gdGhlIGZyb20ncyBiYWxhbmNlLiBUaGUgc3BlbmRlcidzIGFsbG93YW5jZSBvbiBmcm9tJ3MgYmFsYW5jZQp3aWxsIGJlIHJlZHVjZWQgYnkgdGhlIGFtb3VudC4KCiMgQXJndW1lbnRzCgoqIGBzcGVuZGVyYCAtIFRoZSBhZGRyZXNzIGF1dGhvcml6aW5nIHRoZSB0cmFuc2ZlciwgYW5kIGhhdmluZyBpdHMKYWxsb3dhbmNlIGNvbnN1bWVkIGR1cmluZyB0aGUgdHJhbnNmZXIuCiogYGZyb21gIC0gVGhlIGFkZHJlc3MgaG9sZGluZyB0aGUgYmFsYW5jZSBvZiB0b2tlbnMgd2hpY2ggd2lsbCBiZQp3aXRoZHJhd24gZnJvbS4KKiBgdG9gIC0gVGhlIGFkZHJlc3Mgd2hpY2ggd2lsbCByZWNlaXZlIHRoZSB0cmFuc2ZlcnJlZCB0b2tlbnMuCiogYGFtb3VudGAgLSBUaGUgYW1vdW50IG9mIHRva2VucyB0byBiZSB0cmFuc2ZlcnJlZC4KCiMgRXZlbnRzCgpFbWl0cyBhbiBldmVudCB3aXRoIHRvcGljcyBgWyJ0cmFuc2ZlciIsIGZyb206IEFkZHJlc3MsIHRvOiBBZGRyZXNzXSwKZGF0YSA9IGFtb3VudDogaTEyOGAAAAAAAAANdHJhbnNmZXJfZnJvbQAAAAAAAAQAAAAAAAAAB3NwZW5kZXIAAAAAEwAAAAAAAAAEZnJvbQAAABMAAAAAAAAAAnRvAAAAAAATAAAAAAAAAAZhbW91bnQAAAAAAAsAAAAA" + unpacker = xdrlib3.Unpacker(base64.b64decode(raw_xdr)) + specs = [] + while unpacker.get_position() < len(base64.b64decode(raw_xdr)): + specs.append(xdr.SCSpecEntry.unpack(unpacker)) + return specs diff --git a/stellar_contract_bindings/python.py b/stellar_contract_bindings/python.py index 2f070e3..1300a4d 100644 --- a/stellar_contract_bindings/python.py +++ b/stellar_contract_bindings/python.py @@ -1,3 +1,4 @@ +import keyword import os from typing import List @@ -7,11 +8,7 @@ from stellar_sdk import xdr from stellar_contract_bindings import __version__ as stellar_contract_bindings_version -from stellar_contract_bindings.metadata import parse_contract_metadata -from stellar_contract_bindings.utils import ( - get_wasm_hash_by_contract_id, - get_contract_wasm_by_hash, -) +from stellar_contract_bindings.utils import get_specs_by_contract_id def is_tuple_struct(entry: xdr.SCSpecUDTStructV0) -> bool: @@ -539,13 +536,63 @@ def parse_result_xdr_fn(output: List[xdr.SCSpecTypeDef]): return client_rendered_code -def generate_binding(wasm: bytes, client_type: str) -> str: - generated = [] - generated.append(render_info()) +# append _ to keyword +def append_underscore(specs: List[xdr.SCSpecEntry]): + for spec in specs: + if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_STRUCT_V0: + assert spec.udt_struct_v0 is not None + if keyword.iskeyword(spec.udt_struct_v0.name.decode()): + spec.udt_struct_v0.name = spec.udt_struct_v0.name + b"_" + for field in spec.udt_struct_v0.fields: + if keyword.iskeyword(field.name.decode()): + field.name = field.name + b"_" + if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_UNION_V0: + assert spec.udt_union_v0 is not None + if keyword.iskeyword(spec.udt_union_v0.name.decode()): + spec.udt_union_v0.name = spec.udt_union_v0.name + b"_" + for union_case in spec.udt_union_v0.cases: + if ( + union_case.kind + == xdr.SCSpecUDTUnionCaseV0Kind.SC_SPEC_UDT_UNION_CASE_TUPLE_V0 + ): + if keyword.iskeyword(union_case.tuple_case.name.decode()): + union_case.tuple_case.name = union_case.tuple_case.name + b"_" + elif ( + union_case.kind + == xdr.SCSpecUDTUnionCaseV0Kind.SC_SPEC_UDT_UNION_CASE_VOID_V0 + ): + if keyword.iskeyword(union_case.void_case.name.decode()): + union_case.void_case.name = union_case.void_case.name + b"_" + else: + raise ValueError(f"Unsupported union case kind: {union_case.kind}") + if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_FUNCTION_V0: + assert spec.function_v0 is not None + if keyword.iskeyword(spec.function_v0.name.sc_symbol.decode()): + spec.function_v0.name.sc_symbol = spec.function_v0.name.sc_symbol + b"_" + for param in spec.function_v0.inputs: + if keyword.iskeyword(param.name.decode()): + param.name = param.name + b"_" + if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ENUM_V0: + assert spec.udt_enum_v0 is not None + if keyword.iskeyword(spec.udt_enum_v0.name.decode()): + spec.udt_enum_v0.name = spec.udt_enum_v0.name + b"_" + for enum_case in spec.udt_enum_v0.cases: + if keyword.iskeyword(enum_case.name.decode()): + enum_case.name = enum_case.name + b"_" + if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ERROR_ENUM_V0: + assert spec.udt_error_enum_v0 is not None + if keyword.iskeyword(spec.udt_error_enum_v0.name.decode()): + spec.udt_error_enum_v0.name = spec.udt_error_enum_v0.name + b"_" + for error_enum_case in spec.udt_error_enum_v0.cases: + if keyword.iskeyword(error_enum_case.name.decode()): + error_enum_case.name = error_enum_case.name + b"_" + - metadata = parse_contract_metadata(wasm) - specs = metadata.spec +def generate_binding(specs: List[xdr.SCSpecEntry], client_type: str) -> str: + append_underscore(specs) + generated = [] + generated.append(render_info()) generated.append(render_imports(client_type)) for spec in specs: @@ -599,16 +646,13 @@ def command(contract_id: str, rpc_url: str, output: str, client_type: str): if output is None: output = os.getcwd() try: - wasm_id = get_wasm_hash_by_contract_id(contract_id, rpc_url) - click.echo(f"Got wasm id: {wasm_id.hex()}") - wasm_code = get_contract_wasm_by_hash(wasm_id, rpc_url) - click.echo(f"Got wasm code") + specs = get_specs_by_contract_id(contract_id, rpc_url) except Exception as e: - click.echo(f"Error: {str(e)}", err=True) + click.echo(f"Get contract specs failed: {e}", err=True) raise click.Abort() click.echo("Generating Python bindings") - generated = generate_binding(wasm_code, client_type=client_type) + generated = generate_binding(specs, client_type=client_type) if not os.path.exists(output): os.makedirs(output) output_path = os.path.join(output, "bindings.py") @@ -621,8 +665,12 @@ def command(contract_id: str, rpc_url: str, output: str, client_type: str): if __name__ == "__main__": + from stellar_contract_bindings.metadata import parse_contract_metadata + wasm_file = "/Users/overcat/repo/lightsail/stellar-contract-bindings/tests/contracts/target/wasm32-unknown-unknown/release/python.wasm" with open(wasm_file, "rb") as f: wasm = f.read() - generated_code = generate_binding(wasm, "both") - print(generated_code) + + specs = parse_contract_metadata(wasm).spec + generated = generate_binding(specs, client_type="both") + print(generated) diff --git a/stellar_contract_bindings/utils.py b/stellar_contract_bindings/utils.py index 9a2697a..e8ac0de 100644 --- a/stellar_contract_bindings/utils.py +++ b/stellar_contract_bindings/utils.py @@ -1,8 +1,14 @@ +from typing import List + from stellar_sdk import SorobanServer from stellar_sdk import xdr, Address +from stellar_contract_bindings.metadata import ( + parse_contract_metadata, + get_token_sc_spec_entry, +) -def get_contract_wasm_by_hash(wasm_hash: bytes, rpc_url: str) -> bytes: +def get_specs_by_wasm_hash(wasm_hash: bytes, rpc_url: str) -> list[xdr.SCSpecEntry]: """Get the contract wasm by wasm hash. :param wasm_hash: The wasm hash. @@ -19,10 +25,11 @@ def get_contract_wasm_by_hash(wasm_hash: bytes, rpc_url: str) -> bytes: if not resp.entries: raise ValueError(f"Wasm not found, wasm id: {wasm_hash.hex()}") data = xdr.LedgerEntryData.from_xdr(resp.entries[0].xdr) - return data.contract_code.code + meta_data = data.contract_code.code + return parse_contract_metadata(meta_data).spec -def get_wasm_hash_by_contract_id(contract_id: str, rpc_url: str) -> bytes: +def get_specs_by_contract_id(contract_id: str, rpc_url: str) -> list[xdr.SCSpecEntry]: """Get the wasm hash by contract id. :param contract_id: The contract id. @@ -43,4 +50,26 @@ def get_wasm_hash_by_contract_id(contract_id: str, rpc_url: str) -> bytes: if not resp.entries: raise ValueError(f"Contract not found, contract id: {contract_id}") data = xdr.LedgerEntryData.from_xdr(resp.entries[0].xdr) - return data.contract_data.val.instance.executable.wasm_hash.hash + if ( + data.contract_data.val.instance.executable.type + == xdr.ContractExecutableType.CONTRACT_EXECUTABLE_STELLAR_ASSET + ): + return get_token_sc_spec_entry() + elif ( + data.contract_data.val.instance.executable.type + == xdr.ContractExecutableType.CONTRACT_EXECUTABLE_WASM + ): + return get_specs_by_wasm_hash( + data.contract_data.val.instance.executable.wasm_hash.hash, rpc_url + ) + else: + raise ValueError( + f"Unknown executable type, type: {data.contract_data.val.instance.executable.type}" + ) + + +if __name__ == "__main__": + get_specs_by_contract_id( + "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA", + "https://mainnet.sorobanrpc.com", + ) diff --git a/tests/client.py b/tests/client.py index d814397..58e6f02 100644 --- a/tests/client.py +++ b/tests/client.py @@ -241,6 +241,97 @@ def from_scval(cls, val: xdr.SCVal): return cls(scval.from_uint32(val)) +class True_: + """This is from the rust doc above the struct SimpleStruct""" + + def_: int + + def __init__(self, def_: int): + self.def_ = def_ + + def to_scval(self) -> xdr.SCVal: + return scval.to_struct({"def_": scval.to_uint32(self.def_)}) + + @classmethod + def from_scval(cls, val: xdr.SCVal): + elements = scval.from_struct(val) + return cls(scval.from_uint32(elements["def_"])) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, True_): + return NotImplemented + return self.def_ == other.def_ + + def __hash__(self) -> int: + return hash((self.def_)) + + +class False_(IntEnum): + elif_ = 1 + + def to_scval(self) -> xdr.SCVal: + return scval.to_uint32(self.value) + + @classmethod + def from_scval(cls, val: xdr.SCVal): + return cls(scval.from_uint32(val)) + + +class None_Kind(Enum): + elif_ = "elif_" + nonlocal_ = "nonlocal_" + not_ = "not_" + + +class None_: + def __init__( + self, + kind: None_Kind, + ): + self.kind = kind + + def to_scval(self) -> xdr.SCVal: + if self.kind == None_Kind.elif_: + return scval.to_enum(self.kind.name, None) + if self.kind == None_Kind.nonlocal_: + return scval.to_enum(self.kind.name, None) + if self.kind == None_Kind.not_: + return scval.to_enum(self.kind.name, None) + + @classmethod + def from_scval(cls, val: xdr.SCVal): + elements = scval.from_enum(val) + kind = None_Kind(elements[0]) + if kind == None_Kind.elif_: + return cls(kind) + if kind == None_Kind.nonlocal_: + return cls(kind) + if kind == None_Kind.not_: + return cls(kind) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, None_): + return NotImplemented + if self.kind != other.kind: + return False + return True + + def __hash__(self) -> int: + return hash(self.kind) + + +class import_(IntEnum): + not_ = 11 + elif_ = 12 + + def to_scval(self) -> xdr.SCVal: + return scval.to_uint32(self.value) + + @classmethod + def from_scval(cls, val: xdr.SCVal): + return cls(scval.from_uint32(val)) + + class Client(ContractClient): def hello( self, @@ -266,6 +357,30 @@ def hello( restore=restore, ) + def from_( + self, + finally_: str, + source: Union[str, MuxedAccount] = NULL_ACCOUNT, + signer: Optional[Keypair] = None, + base_fee: int = 100, + transaction_timeout: int = 300, + submit_timeout: int = 30, + simulate: bool = True, + restore: bool = True, + ) -> AssembledTransaction[str]: + return self.invoke( + "from_", + [scval.to_symbol(finally_)], + parse_result_xdr_fn=lambda v: scval.from_symbol(v), + source=source, + signer=signer, + base_fee=base_fee, + transaction_timeout=transaction_timeout, + submit_timeout=submit_timeout, + simulate=simulate, + restore=restore, + ) + def void( self, source: Union[str, MuxedAccount] = NULL_ACCOUNT, @@ -1092,6 +1207,30 @@ async def hello( restore=restore, ) + async def from_( + self, + finally_: str, + source: Union[str, MuxedAccount] = NULL_ACCOUNT, + signer: Optional[Keypair] = None, + base_fee: int = 100, + transaction_timeout: int = 300, + submit_timeout: int = 30, + simulate: bool = True, + restore: bool = True, + ) -> AssembledTransactionAsync[str]: + return await self.invoke( + "from_", + [scval.to_symbol(finally_)], + parse_result_xdr_fn=lambda v: scval.from_symbol(v), + source=source, + signer=signer, + base_fee=base_fee, + transaction_timeout=transaction_timeout, + submit_timeout=submit_timeout, + simulate=simulate, + restore=restore, + ) + async def void( self, source: Union[str, MuxedAccount] = NULL_ACCOUNT, diff --git a/tests/contracts/contracts/python/src/lib.rs b/tests/contracts/contracts/python/src/lib.rs index 6855a6b..6e5bd25 100644 --- a/tests/contracts/contracts/python/src/lib.rs +++ b/tests/contracts/contracts/python/src/lib.rs @@ -51,12 +51,50 @@ pub enum Error { /// Please provide an odd number NumberMustBeOdd = 1, } + +// Test Python keywords + +/// This is from the rust doc above the struct SimpleStruct +#[contracttype] +pub struct True { + pub def: u32, +} + +#[contracterror] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[repr(u32)] +pub enum False { + /// Please provide an odd number + elif = 1, +} + +#[contracttype] +pub enum None { + elif, + nonlocal, + not, +} + +#[contracttype] +#[derive(Clone, Copy)] +// The `repr` attribute is here to specify the memory alignment for this type +#[repr(u32)] +pub enum import { + not = 11, + elif = 12, +} + #[contractimpl] impl Contract { pub fn hello(_env: Env, hello: Symbol) -> Symbol { hello } + pub fn from(_env: Env, finally: Symbol) -> Symbol { + // test python key words + finally + } + pub fn void(_env: Env) { // do nothing } @@ -133,7 +171,7 @@ impl Contract { } /// Negates a boolean value - pub fn not_(_env: Env, boolean: bool) -> bool { + pub fn not(_env: Env, boolean: bool) -> bool { !boolean } @@ -190,7 +228,10 @@ impl Contract { tuple_strukt } - pub fn tuple_strukt_nested(_env: Env, tuple_strukt: (SimpleStruct, SimpleEnum)) -> (SimpleStruct, SimpleEnum) { + pub fn tuple_strukt_nested( + _env: Env, + tuple_strukt: (SimpleStruct, SimpleEnum), + ) -> (SimpleStruct, SimpleEnum) { tuple_strukt } diff --git a/tests/test_client.py b/tests/test_client.py index 48b7fd7..20de58e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -6,7 +6,7 @@ from .client import * -CONTRACT_ID = "CAYIWC3Y2KK4FXINTF3QFNBOUHGJ673QNG46EX462EYR4EYCFSYVAR4O" +CONTRACT_ID = "CDO5YMIS42WFUXTNMM27MEDAKBQESPCZ7LEC2C4IFUELX3WHCHCS5QVI" RPC_URL = "https://soroban-testnet.stellar.org" NETWORK_PASSPHRASE = Network.TESTNET_NETWORK_PASSPHRASE From 4e1ca67eb60d4d485d7f2272de1aefc3af98d105 Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Fri, 29 Nov 2024 14:46:45 +0800 Subject: [PATCH 2/3] Add support for generating bindings for token contracts. --- stellar_contract_bindings/python.py | 9 +++++++-- tests/client.py | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/stellar_contract_bindings/python.py b/stellar_contract_bindings/python.py index 1300a4d..9e25352 100644 --- a/stellar_contract_bindings/python.py +++ b/stellar_contract_bindings/python.py @@ -491,7 +491,7 @@ def {{ entry.name.sc_symbol.decode() }}(self, {% for param in entry.inputs %}{{ {%- if entry.doc %} """{{ entry.doc.decode() }}""" {%- endif %} - return self.invoke('{{ entry.name.sc_symbol.decode() }}', [{% for param in entry.inputs %}{{ to_scval(param.type, param.name.decode()) }}{% if not loop.last %}, {% endif %}{% endfor %}], parse_result_xdr_fn={{ parse_result_xdr_fn(entry.outputs) }}, source = source, signer = signer, base_fee = base_fee, transaction_timeout = transaction_timeout, submit_timeout = submit_timeout, simulate = simulate, restore = restore) + return self.invoke('{{ entry.name.sc_symbol_r.decode() if entry.name.sc_symbol_r else entry.name.sc_symbol.decode() }}', [{% for param in entry.inputs %}{{ to_scval(param.type, param.name.decode()) }}{% if not loop.last %}, {% endif %}{% endfor %}], parse_result_xdr_fn={{ parse_result_xdr_fn(entry.outputs) }}, source = source, signer = signer, base_fee = base_fee, transaction_timeout = transaction_timeout, submit_timeout = submit_timeout, simulate = simulate, restore = restore) {%- endfor %} {%- endif %} @@ -502,7 +502,7 @@ async def {{ entry.name.sc_symbol.decode() }}(self, {% for param in entry.inputs {%- if entry.doc %} """{{ entry.doc.decode() }}""" {%- endif %} - return await self.invoke('{{ entry.name.sc_symbol.decode() }}', [{% for param in entry.inputs %}{{ to_scval(param.type, param.name.decode()) }}{% if not loop.last %}, {% endif %}{% endfor %}], parse_result_xdr_fn={{ parse_result_xdr_fn(entry.outputs) }}, source = source, signer = signer, base_fee = base_fee, transaction_timeout = transaction_timeout, submit_timeout = submit_timeout, simulate = simulate, restore = restore) + return await self.invoke('{{ entry.name.sc_symbol_r.decode() if entry.name.sc_symbol_r else entry.name.sc_symbol.decode() }}', [{% for param in entry.inputs %}{{ to_scval(param.type, param.name.decode()) }}{% if not loop.last %}, {% endif %}{% endfor %}], parse_result_xdr_fn={{ parse_result_xdr_fn(entry.outputs) }}, source = source, signer = signer, base_fee = base_fee, transaction_timeout = transaction_timeout, submit_timeout = submit_timeout, simulate = simulate, restore = restore) {%- endfor %} {%- endif %} ''' @@ -542,6 +542,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_STRUCT_V0: assert spec.udt_struct_v0 is not None if keyword.iskeyword(spec.udt_struct_v0.name.decode()): + spec.udt_struct_v0.name_r = spec.udt_struct_v0.name spec.udt_struct_v0.name = spec.udt_struct_v0.name + b"_" for field in spec.udt_struct_v0.fields: if keyword.iskeyword(field.name.decode()): @@ -549,6 +550,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_UNION_V0: assert spec.udt_union_v0 is not None if keyword.iskeyword(spec.udt_union_v0.name.decode()): + spec.udt_union_v0.name_r = spec.udt_union_v0.name spec.udt_union_v0.name = spec.udt_union_v0.name + b"_" for union_case in spec.udt_union_v0.cases: if ( @@ -568,6 +570,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_FUNCTION_V0: assert spec.function_v0 is not None if keyword.iskeyword(spec.function_v0.name.sc_symbol.decode()): + spec.function_v0.name.sc_symbol_r = spec.function_v0.name.sc_symbol spec.function_v0.name.sc_symbol = spec.function_v0.name.sc_symbol + b"_" for param in spec.function_v0.inputs: if keyword.iskeyword(param.name.decode()): @@ -575,6 +578,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ENUM_V0: assert spec.udt_enum_v0 is not None if keyword.iskeyword(spec.udt_enum_v0.name.decode()): + spec.udt_enum_v0.name_r = spec.udt_enum_v0.name spec.udt_enum_v0.name = spec.udt_enum_v0.name + b"_" for enum_case in spec.udt_enum_v0.cases: if keyword.iskeyword(enum_case.name.decode()): @@ -582,6 +586,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ERROR_ENUM_V0: assert spec.udt_error_enum_v0 is not None if keyword.iskeyword(spec.udt_error_enum_v0.name.decode()): + spec.udt_error_enum_v0.name_r = spec.udt_error_enum_v0.name spec.udt_error_enum_v0.name = spec.udt_error_enum_v0.name + b"_" for error_enum_case in spec.udt_error_enum_v0.cases: if keyword.iskeyword(error_enum_case.name.decode()): diff --git a/tests/client.py b/tests/client.py index 58e6f02..c8a8678 100644 --- a/tests/client.py +++ b/tests/client.py @@ -369,7 +369,7 @@ def from_( restore: bool = True, ) -> AssembledTransaction[str]: return self.invoke( - "from_", + "from", [scval.to_symbol(finally_)], parse_result_xdr_fn=lambda v: scval.from_symbol(v), source=source, @@ -781,7 +781,7 @@ def not_( ) -> AssembledTransaction[bool]: """Negates a boolean value""" return self.invoke( - "not_", + "not", [scval.to_bool(boolean)], parse_result_xdr_fn=lambda v: scval.from_bool(v), source=source, @@ -1219,7 +1219,7 @@ async def from_( restore: bool = True, ) -> AssembledTransactionAsync[str]: return await self.invoke( - "from_", + "from", [scval.to_symbol(finally_)], parse_result_xdr_fn=lambda v: scval.from_symbol(v), source=source, @@ -1631,7 +1631,7 @@ async def not_( ) -> AssembledTransactionAsync[bool]: """Negates a boolean value""" return await self.invoke( - "not_", + "not", [scval.to_bool(boolean)], parse_result_xdr_fn=lambda v: scval.from_bool(v), source=source, From 63f5d5d5d360bd10d7ee655919492a5b0a3cef49 Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Fri, 29 Nov 2024 15:11:34 +0800 Subject: [PATCH 3/3] WIP --- stellar_contract_bindings/python.py | 20 +++++++++++--------- tests/client.py | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/stellar_contract_bindings/python.py b/stellar_contract_bindings/python.py index 9e25352..b47f333 100644 --- a/stellar_contract_bindings/python.py +++ b/stellar_contract_bindings/python.py @@ -297,7 +297,7 @@ def __init__(self, {% for field in entry.fields %}{{ field.name.decode() }}: {{ def to_scval(self) -> xdr.SCVal: return scval.to_struct({ {%- for field in entry.fields %} - '{{ field.name.decode() }}': {{ to_scval(field.type, 'self.' ~ field.name.decode()) }}{% if not loop.last %},{% endif %} + '{{ field.name_r.decode() if field.name_r else field.name.decode() }}': {{ to_scval(field.type, 'self.' ~ field.name.decode()) }}{% if not loop.last %},{% endif %} {%- endfor %} }) @@ -306,7 +306,7 @@ def from_scval(cls, val: xdr.SCVal): elements = scval.from_struct(val) return cls( {%- for index, field in enumerate(entry.fields) %} - {{ from_scval(field.type, 'elements["' ~ field.name.decode() ~ '"]') }}{% if not loop.last %},{% endif %} + {{ from_scval(field.type, 'elements["' ~ (field.name_r.decode() if field.name_r else field.name.decode()) ~ '"]') }}{% if not loop.last %},{% endif %} {%- endfor %} ) @@ -368,9 +368,9 @@ def render_union(entry: xdr.SCSpecUDTUnionV0): class {{ entry.name.decode() }}Kind(Enum): {%- for case in entry.cases %} {%- if case.kind == xdr.SCSpecUDTUnionCaseV0Kind.SC_SPEC_UDT_UNION_CASE_VOID_V0 %} - {{ case.void_case.name.decode() }} = '{{ case.void_case.name.decode() }}' + {{ case.void_case.name.decode() }} = '{{ case.void_case.name_r.decode() if case.void_case.name_r else case.void_case.name.decode() }}' {%- else %} - {{ case.tuple_case.name.decode() }} = '{{ case.tuple_case.name.decode() }}' + {{ case.tuple_case.name.decode() }} = '{{ case.tuple_case.name.decode() if case.tuple_case.name_r else case.tuple_case.name.decode() }}' {%- endif %} {%- endfor %} """ @@ -542,7 +542,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_STRUCT_V0: assert spec.udt_struct_v0 is not None if keyword.iskeyword(spec.udt_struct_v0.name.decode()): - spec.udt_struct_v0.name_r = spec.udt_struct_v0.name + spec.udt_struct_v0.name_r = spec.udt_struct_v0.name # type: ignore[attr-defined] spec.udt_struct_v0.name = spec.udt_struct_v0.name + b"_" for field in spec.udt_struct_v0.fields: if keyword.iskeyword(field.name.decode()): @@ -550,7 +550,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_UNION_V0: assert spec.udt_union_v0 is not None if keyword.iskeyword(spec.udt_union_v0.name.decode()): - spec.udt_union_v0.name_r = spec.udt_union_v0.name + spec.udt_union_v0.name_r = spec.udt_union_v0.name # type: ignore[attr-defined] spec.udt_union_v0.name = spec.udt_union_v0.name + b"_" for union_case in spec.udt_union_v0.cases: if ( @@ -558,19 +558,21 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): == xdr.SCSpecUDTUnionCaseV0Kind.SC_SPEC_UDT_UNION_CASE_TUPLE_V0 ): if keyword.iskeyword(union_case.tuple_case.name.decode()): + union_case.tuple_case.name_r = union_case.tuple_case.name # type: ignore[attr-defined] union_case.tuple_case.name = union_case.tuple_case.name + b"_" elif ( union_case.kind == xdr.SCSpecUDTUnionCaseV0Kind.SC_SPEC_UDT_UNION_CASE_VOID_V0 ): if keyword.iskeyword(union_case.void_case.name.decode()): + union_case.void_case.name_r = union_case.void_case.name # type: ignore[attr-defined] union_case.void_case.name = union_case.void_case.name + b"_" else: raise ValueError(f"Unsupported union case kind: {union_case.kind}") if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_FUNCTION_V0: assert spec.function_v0 is not None if keyword.iskeyword(spec.function_v0.name.sc_symbol.decode()): - spec.function_v0.name.sc_symbol_r = spec.function_v0.name.sc_symbol + spec.function_v0.name.sc_symbol_r = spec.function_v0.name.sc_symbol # type: ignore[attr-defined] spec.function_v0.name.sc_symbol = spec.function_v0.name.sc_symbol + b"_" for param in spec.function_v0.inputs: if keyword.iskeyword(param.name.decode()): @@ -578,7 +580,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ENUM_V0: assert spec.udt_enum_v0 is not None if keyword.iskeyword(spec.udt_enum_v0.name.decode()): - spec.udt_enum_v0.name_r = spec.udt_enum_v0.name + spec.udt_enum_v0.name_r = spec.udt_enum_v0.name # type: ignore[attr-defined] spec.udt_enum_v0.name = spec.udt_enum_v0.name + b"_" for enum_case in spec.udt_enum_v0.cases: if keyword.iskeyword(enum_case.name.decode()): @@ -586,7 +588,7 @@ def append_underscore(specs: List[xdr.SCSpecEntry]): if spec.kind == xdr.SCSpecEntryKind.SC_SPEC_ENTRY_UDT_ERROR_ENUM_V0: assert spec.udt_error_enum_v0 is not None if keyword.iskeyword(spec.udt_error_enum_v0.name.decode()): - spec.udt_error_enum_v0.name_r = spec.udt_error_enum_v0.name + spec.udt_error_enum_v0.name_r = spec.udt_error_enum_v0.name # type: ignore[attr-defined] spec.udt_error_enum_v0.name = spec.udt_error_enum_v0.name + b"_" for error_enum_case in spec.udt_error_enum_v0.cases: if keyword.iskeyword(error_enum_case.name.decode()): diff --git a/tests/client.py b/tests/client.py index c8a8678..1121bdf 100644 --- a/tests/client.py +++ b/tests/client.py @@ -278,9 +278,9 @@ def from_scval(cls, val: xdr.SCVal): class None_Kind(Enum): - elif_ = "elif_" - nonlocal_ = "nonlocal_" - not_ = "not_" + elif_ = "elif" + nonlocal_ = "nonlocal" + not_ = "not" class None_: