Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: getattr and iter dunders #42

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ repos:
rev: 0.7.17
hooks:
- id: mdformat
additional_dependencies: [mdformat-gfm, mdformat-frontmatter]
additional_dependencies: [mdformat-gfm, mdformat-frontmatter, mdformat-pyproject]

default_language_version:
python: python3
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ Lastly, to get information about a token, including its contract address, you ca
from ape_tokens import tokens

bat = tokens["BAT"]
# Or, access via attribute
bat = tokens.BAT

print(bat.address)
```
35 changes: 27 additions & 8 deletions ape_tokens/managers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Iterator, Mapping

from ape.contracts import ContractInstance
from ape.exceptions import ContractNotFoundError
from ape.types import ContractType
Expand Down Expand Up @@ -106,22 +108,33 @@
)


class TokenManager(ManagerAccessMixin, dict):
class TokenManager(ManagerAccessMixin, Mapping[str, ContractInstance]):
@cached_property
def _manager(self) -> TokenListManager:
return TokenListManager()

def __repr__(self) -> str:
return f"<ape_tokens.TokenManager default='{self._manager.default_tokenlist}'>"

def __getitem__(self, symbol: str) -> ContractInstance:
try:
token_info = self._manager.get_token_info(
symbol, chain_id=self.network_manager.network.chain_id
)
def __len__(self) -> int:
return len(list(self._manager.get_tokens(chain_id=self.provider.chain_id)))

except ValueError as err:
raise KeyError(f"Symbol '{symbol}' is not a known token symbol") from err
def __iter__(self) -> Iterator[ContractInstance]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried running:

[x for x in tokens]

but got this:

KeyError: "Symbol 'AZRX' is not a known token symbol"

In [2]: [x for x in tokens]

kinda weird. I do have multiple token lists installed though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if something like:

 def __iter__(self) -> Iterator[ContractInstance]:
        yielded = set()
        for token in self._manager.get_tokens(chain_id=self.provider.chain_id):
            if token.symbol in yielded:
                # Handle multiple lists.
                continue

            try:
                yield self[token.symbol]
            except KeyError:
                # Perhaps in another list?
                continue

            yielded.add(token.symbol)

would work for this situation.
I tried it again, except changed it to:

for x in tokens:
   ...:     print(x)

I see the first like hundred get printed out super fast, and then it suddenly is very slow at outputting them.
hmmmmm

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible the slowness is from downloading the last of the tokenlists? Looking at this code again I'm not sure where else a slowdown might come from.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be fixed now with my latest commit.

for token in self._manager.get_tokens(chain_id=self.provider.chain_id):
Copy link
Member

@antazoey antazoey May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am getting the KeyError here when doing [x for x in token]

In [1]: [x for x in tokens]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[1], line 1
----> 1 [x for x in tokens]

File ~/PycharmProjects/ape-tokens/ape_tokens/managers.py:124, in TokenManager.__iter__(self)
    122 def __iter__(self) -> Iterator[ContractInstance]:
    123     for token in self._manager.get_tokens(chain_id=self.provider.chain_id):
--> 124         yield self[token.symbol]

File ~/PycharmProjects/ape-tokens/ape_tokens/managers.py:137, in TokenManager.__getitem__(self, symbol)
    134         continue
    136 if token_info is None:
--> 137     raise KeyError(f"Symbol '{symbol}' is not a known token symbol")
    139 checksummed_address = to_checksum_address(token_info.address)
    140 try:

KeyError: "Symbol 'GETH' is not a known token symbol"

yield self[token.symbol]

def __getitem__(self, symbol: str) -> ContractInstance:
token_info = None
for tokenlist in self._manager.available_tokenlists():
try:
token_info = self._manager.get_token_info(
symbol, chain_id=self.network_manager.network.chain_id, token_listname=tokenlist
)
except ValueError:
BobTheBuidler marked this conversation as resolved.
Show resolved Hide resolved
continue

if token_info is None:
raise KeyError(f"Symbol '{symbol}' is not a known token symbol")

checksummed_address = to_checksum_address(token_info.address)
try:
Expand All @@ -130,3 +143,9 @@ def __getitem__(self, symbol: str) -> ContractInstance:
return self.chain_manager.contracts.instance_at(
checksummed_address, contract_type=ERC20
)

def __getattr__(self, symbol: str) -> ContractInstance:
try:
return self[symbol]
except KeyError as e:
raise AttributeError(str(e)) from None