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: added ape cache plugin #680

Merged
merged 133 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
84be3b0
feat: added cache query
johnson2427 Apr 29, 2022
85aced2
chore: reformatting following pre-commit
johnson2427 Apr 29, 2022
81de5be
fix: mypy issues
johnson2427 Apr 29, 2022
73d6b84
fix: used more secure way to query the database, and removed TODO
johnson2427 Apr 29, 2022
f35234e
fix: mypy and flake8 issues
johnson2427 Apr 29, 2022
e25d58d
chore: clean up of docstrings
johnson2427 Apr 29, 2022
019446d
fix: blocks database structure, need to work on updating by column
johnson2427 Apr 29, 2022
0ec9b92
feat: moved init_db and purge_db functionality to CacheQueryProvider
johnson2427 Apr 29, 2022
1f5373c
feat: handles missing columns, database not existing, no nullable dat…
johnson2427 Apr 29, 2022
50105d3
chore: pre-commit
johnson2427 Apr 29, 2022
f2129fd
fix: cannot import Column from sqlalchemy.types, fixed this in models…
johnson2427 May 2, 2022
89ea5a5
chore: linting
johnson2427 May 2, 2022
1c6f969
fix: added sqlalchemy to setup.py
johnson2427 May 2, 2022
646ab7a
feat: removed schemas.py, no need for pydantic here, can go to the AP…
johnson2427 May 2, 2022
e865c8f
feat: allow to cache all data during query no matter what column you …
johnson2427 May 4, 2022
ef6b39a
mypy issue
johnson2427 May 4, 2022
b641594
removed breakpoint
johnson2427 May 4, 2022
ae4fb72
removed TODO columns are no longer going to be handled in the update …
johnson2427 May 5, 2022
a035187
moving DefaultQueryProvider back into managers/query.py
johnson2427 May 11, 2022
4e47e31
type ignore for logger due to mypy
johnson2427 May 11, 2022
2313ef9
fix: fixed query so it doesn't default to cache
johnson2427 May 11, 2022
2697823
chore: type ignore issue
johnson2427 May 12, 2022
5af8007
fix: importing logger from ape.logging
johnson2427 May 12, 2022
b15a1ac
fix: isort issue
johnson2427 May 12, 2022
932f20c
feat: simplified logic in estimate_block_query
johnson2427 May 12, 2022
4541207
fix: docs build issue
johnson2427 May 12, 2022
a732467
fix: docs issue
johnson2427 May 12, 2022
53195d6
fix: importing logger mistake
johnson2427 May 12, 2022
a589534
refactor: simplify logic in ape_cache query.py
johnson2427 May 17, 2022
95ca512
feat: caching data added
johnson2427 Jul 11, 2022
0ffddeb
fix: mypy issues
johnson2427 Jul 11, 2022
0d5367f
fix: isort and mypy
johnson2427 Jul 11, 2022
359d2c0
fix: mypy
johnson2427 Jul 11, 2022
7905f18
fix: documentation for `update_cache`
johnson2427 Jul 11, 2022
fcc99bb
fix: mypy
johnson2427 Jul 11, 2022
622d585
fix: mypy
johnson2427 Jul 11, 2022
cfad432
fix: type in update cache
johnson2427 Jul 12, 2022
4c0a9e8
fix: type in update cache
johnson2427 Jul 12, 2022
2246577
fix: remove ignore
johnson2427 Jul 12, 2022
9e44467
feat: added transactions cache
johnson2427 Jul 12, 2022
07f036d
feat: created singledispatch for cache
johnson2427 Jul 13, 2022
e2d29f2
refactor: remove ContractEvents for now
johnson2427 Jul 14, 2022
cac191c
fix: cache was failing
johnson2427 Aug 3, 2022
442d252
feat: added contract_events table to caching system
johnson2427 Aug 3, 2022
187d30e
fix: mypy
johnson2427 Aug 3, 2022
7008138
refactor: remove print
johnson2427 Aug 3, 2022
216b582
feat: major upgrade to CacheQueryProvider
johnson2427 Aug 4, 2022
64031b1
fix: mypy issue and linters
johnson2427 Aug 4, 2022
9c4c8ac
fix: remove print
johnson2427 Aug 4, 2022
bcfb5ee
feat: added raises
johnson2427 Aug 4, 2022
6bc00a2
fix: added missing argument to cache_query
johnson2427 Aug 4, 2022
2cf38cd
feat: added test and set logger to error
johnson2427 Aug 5, 2022
c9c0e5f
feat: removed type ignore for sqlalchemy imports and added cache.md
johnson2427 Aug 5, 2022
5ce332d
fix: fixed markdowns and fixed issue where db was being created witho…
johnson2427 Aug 6, 2022
c90174d
fix: pytest failure because of raises in database_file method
johnson2427 Aug 6, 2022
d1f7137
fix: misspelling in index.md
johnson2427 Aug 6, 2022
7388331
fix: query manager was not using iterators
fubuloubu Aug 8, 2022
0117fdb
refactor: major update to how cache provider works
fubuloubu Aug 9, 2022
ff9b619
refactor: update ape cache cli to match
fubuloubu Aug 9, 2022
b256c1e
feat: query is operating as expected
johnson2427 Aug 9, 2022
9bc7b05
fix: caching issue of integers being too large
johnson2427 Aug 9, 2022
b580a1b
fix: type hint
johnson2427 Aug 9, 2022
ea883c0
fix: isort
johnson2427 Aug 9, 2022
77af6a0
fix: mypy
johnson2427 Aug 9, 2022
bf1e854
fix: mypy
johnson2427 Aug 9, 2022
5513a0c
fix: mypy
johnson2427 Aug 9, 2022
d40809e
fix: mypy
johnson2427 Aug 9, 2022
e274e6b
fix: mypy for the last time please
johnson2427 Aug 9, 2022
9733c4e
feat: added suggestions
johnson2427 Aug 9, 2022
fe448eb
fix: formatting markdown and managers.query
johnson2427 Aug 9, 2022
83341e0
fix: suggested changes from fubu
johnson2427 Aug 10, 2022
44118c7
feat: adding errors to logging for QueryEngine
johnson2427 Aug 10, 2022
87f2a42
fix: mypy and added type ignore to perform_query
johnson2427 Aug 10, 2022
3ff69a3
fix: num_transactions in the blocks table is now integer
johnson2427 Aug 10, 2022
80de324
fix: CacheQuery docstring
johnson2427 Aug 10, 2022
d7dbcfd
fix: show warnings instead of errors when db is not inited
johnson2427 Aug 10, 2022
8407b0c
feat: add url for information on QueryEngineError
johnson2427 Aug 10, 2022
9c16db8
feat: add url for information on QueryEngineError
johnson2427 Aug 10, 2022
e905f52
feat: add url for information on QueryEngineError
johnson2427 Aug 10, 2022
b1b4921
fix: remove breakpoint
johnson2427 Aug 10, 2022
7a0a910
feat: moved try for update_cache inside of the with statement
johnson2427 Aug 10, 2022
c9f7cb2
feat: adding txn_hash and signature to Transactions
johnson2427 Aug 10, 2022
bf54b68
feat: removed breakpoint
johnson2427 Aug 11, 2022
41c7f7d
fix: docstring
johnson2427 Aug 11, 2022
91cf184
feat: this would be a breaking change
johnson2427 Aug 11, 2022
d646b43
fix: cache markdown
johnson2427 Aug 11, 2022
f34babb
fix: remove bytes from transaction api
johnson2427 Aug 11, 2022
6ce854f
refactor: remove type ignore
johnson2427 Aug 11, 2022
52a019f
refactor: raise message fix
johnson2427 Aug 11, 2022
acbbe69
refactor: revert transaction api back to original form
johnson2427 Aug 12, 2022
ce2a32e
refactor: remove any from typing
johnson2427 Aug 12, 2022
9ec3b5b
feat: getting all data for transactions
johnson2427 Aug 15, 2022
2450b73
fix: breakpoint removal
johnson2427 Aug 15, 2022
d962f8e
feat: transaction cache full operational
johnson2427 Aug 15, 2022
1a1ee42
fix: type ignores for properties from transaction api
johnson2427 Aug 15, 2022
9e10bea
fix: solve missing data by setting to None
johnson2427 Aug 15, 2022
18da6b8
fix: unused import
johnson2427 Aug 15, 2022
1bceb12
fix: mypy issue with table columns
johnson2427 Aug 15, 2022
df48030
fix: black
johnson2427 Aug 15, 2022
7ad1bd0
fix: wrong order of arguments for estimate query call
fubuloubu Aug 15, 2022
3fa3e25
fix: fetch estimate properly in CacheQueryProvider
fubuloubu Aug 15, 2022
57742d8
fix: wrong estimates in DefaultQueryProvider
fubuloubu Aug 15, 2022
68deb2f
fix: unable to unpack the two different returns here
johnson2427 Aug 15, 2022
0e9cee2
fix: linters
johnson2427 Aug 15, 2022
c43f8be
fix: blocks queries properly
johnson2427 Aug 16, 2022
0229350
refactor: replace info with success messages
johnson2427 Aug 16, 2022
850afae
refactor: adding singledispatchmethod for perform_query
johnson2427 Aug 16, 2022
da97db3
feat: transactions and blocks fully operational
johnson2427 Aug 16, 2022
7200551
feat: handle individual column queries for blocks
johnson2427 Aug 16, 2022
408d1b7
fix: mypy
johnson2427 Aug 16, 2022
41ff63f
feat: properly calculate query time in DefaultQueryProvider
johnson2427 Aug 16, 2022
6ad6229
feat: one liner for the estimate of query
johnson2427 Aug 16, 2022
2fcafcd
fix: cache init and purge messages in test
johnson2427 Aug 16, 2022
d176475
fix: HexBytesString schema type doesn't handle string inputs
fubuloubu Aug 16, 2022
2919c08
fix: updates to ape_ethereum.Block to ensure that values always exist
fubuloubu Aug 16, 2022
5a79e2b
fix: docstring format in HexByteString
johnson2427 Aug 17, 2022
c379f24
feat: added docstrings to all cli methods for ape cache
johnson2427 Aug 17, 2022
513971b
feat: added beta release note to cache markdown file
johnson2427 Aug 17, 2022
79266c6
fix: query and purge docstrings
johnson2427 Aug 17, 2022
e492391
fix: need to convert hash and parent hash to hex in decode_block
johnson2427 Aug 18, 2022
9bf4326
fix: models must return bytes properly from new data type
johnson2427 Aug 18, 2022
0796a4e
refactor: docstrings added to ape_cache.query and cleaned
johnson2427 Aug 18, 2022
3abb045
fix; database_connection should not be hidden
johnson2427 Aug 18, 2022
f03bed1
fix: mypy issue
johnson2427 Aug 18, 2022
8c8b95c
fix: added minor changes to documentation
johnson2427 Aug 18, 2022
21465f7
fix: comments and some logic tweaks
johnson2427 Aug 18, 2022
49b4e3d
fix: perform_transaction_query uses a map
johnson2427 Aug 18, 2022
9aa7663
feat: remove perform_query_clause
johnson2427 Aug 18, 2022
e756692
fix: add required to network_option for init and purge
fubuloubu Aug 18, 2022
ef0e4e6
refactor: raise Error instead of returning None when no cache clause
fubuloubu Aug 18, 2022
f00c44c
feat: better bypass of database when corrupted or not initialized
fubuloubu Aug 18, 2022
44aac00
fix(HACK): bypass BlockAPI.transactions requests until #994
fubuloubu Aug 18, 2022
6c79a10
fix: bypass database connection whenever connected to local
fubuloubu Aug 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
userguides/installing_plugins
userguides/projects
userguides/compile
userguides/cache
userguides/networks
userguides/developing_plugins
userguides/config
Expand Down
69 changes: 69 additions & 0 deletions docs/userguides/cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Cache
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
** Note: This is in Beta release. This functionality is in constant development and many features are in planning stages.
Use the cache plugin to store provider data in a sqlite database.**

```bash
ape cache init --network <ecosystem-name>:<network-name>
```

If you want to set up your network connection for caching use [this guide](./network.html)

```bash
ape cache init --network ethereum:mainnet
```

This creates a SQLite database file in the hidden ape folder.

## Get data from the provider

Use `ape console`:

```bash
ape console --network ethereum:mainnet:infura
```

Run a few queries:

```python
In [1]: chain.blocks.query("*", stop_block=20)
In [2]: chain.blocks[-2].transactions
```

On a deployed contract, you can query events:
- Below, FooHappened is the event from your contract instance that you want to query from.

```python
contract_instance.FooHappened.query("*", start_block=-1)
```

where `contract_instance` is the return of owner.deploy(Contract)
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved

See [this guide](../userguides/contracts.html) for more information how to get a contract instance.

Exit the IPython interpreter.

You can query the cache database directly, for debugging purposes. For example, to get the `blocks` table data from the SQLite db we can do the following:
```bash
ape cache query --network ethereum:mainnet:infura "SELECT * FROM blocks"
Copy link
Contributor

Choose a reason for hiding this comment

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

I follow this guide and everything works until I get to this part.
I am seeing the DBs get created and I do the console step to query blocks from the provider and I see the results show up when I do that.

But then when I get to this part, the CLI does not output anything.

ape cache query --network starknet:testnet "SELECT * FROM blocks
ape cache query --network ethereum:mainnet:infura "SELECT * FROM blocks
ape cache query --network ethereum:mainnet:infura "SELECT * FROM blocks"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't have an answer on why that isn't working for you at the moment, I will review

Copy link
Member

Choose a reason for hiding this comment

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

I think if it doesn't find any data, it will not output anything

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that is the functionality. You said you went through the markdown, but just to confirm, you did run a query through the console before you tried to run the query via ape cache query correct?

Copy link
Contributor

@antazoey antazoey Aug 16, 2022

Choose a reason for hiding this comment

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

Yes, I ran the blocks query from the markdown guide in each of the ecosystem-network combos I tried

```

Returns:
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved

```bash
hash num_transactions number parent_hash size timestamp gas_limit gas_used base_fee difficulty total_difficulty
0 b'\xd4\xe5g@\xf8v\xae\xf8\xc0\x10\xb8j@\xd5\xf... 0 0 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00... 540 0 5000 0 None 17179869184 17179869184
1 b'\x88\xe9mE7\xbe\xa4\xd9\xc0]\x12T\x99\x07\xb... 0 1 b'\xd4\xe5g@\xf8v\xae\xf8\xc0\x10\xb8j@\xd5\xf... 537 1438269988 5000 0 None 17171480576 34351349760
2 b'\xb4\x95\xa1\xd7\xe6f1R\xae\x92p\x8d\xa4\x84... 0 2 b'\x88\xe9mE7\xbe\xa4\xd9\xc0]\x12T\x99\x07\xb... 544 1438270017 5000 0 None 17163096064 51514445824
```

To get `transactions` or `contract_events`:
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved

```bash
ape cache query --network ethereum:mainnet:infura "SELECT * FROM transactions"
```

or

```bash
ape cache query --network ethereum:mainnet:infura "SELECT * FROM contract_events"
```
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
"types-requests", # NOTE: Needed due to mypy typeshed
"types-pkg-resources", # NOTE: Needed due to mypy typeshed
"pandas-stubs==1.2.0.62", # NOTE: Needed due to mypy typeshed
"types-SQLAlchemy>=1.4.49",
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
"flake8>=4.0.1,<5.0", # Style linter
"flake8-breakpoint>=1.1.0,<2.0.0", # detect breakpoints left in code
"flake8-print>=4.0.0,<5.0.0", # detect print statements left in code
"isort>=5.10.1,<6.0", # Import sorting linter
"pandas-stubs==1.2.0.62", # NOTE: Needed due to mypy types
],
"doc": [
"myst-parser>=0.17.0,<0.18", # Tools for parsing markdown files in the docs
Expand Down Expand Up @@ -103,6 +105,7 @@
"pydantic>=1.9.2,<2",
"pygit2>=1.7.2,<2",
"PyGithub>=1.54,<2",
"SQLAlchemy>=1.4.35",
"pytest>=6.0,<8.0",
"python-dateutil>=2.8.2,<3",
"pyyaml>=6.0,<7",
Expand All @@ -129,6 +132,7 @@
"pytest11": ["ape_test=ape.pytest.plugin"],
"ape_cli_subcommands": [
"ape_accounts=ape_accounts._cli:cli",
"ape_cache=ape_cache._cli:cli",
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
"ape_compile=ape_compile._cli:cli",
"ape_console=ape_console._cli:cli",
"ape_plugins=ape_plugins._cli:cli",
Expand Down
1 change: 1 addition & 0 deletions src/ape/__modules__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
__modules__ = [
"ape",
"ape_accounts",
"ape_cache",
"ape_compile",
"ape_console",
"ape_ethereum",
Expand Down
5 changes: 4 additions & 1 deletion src/ape/api/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ class BlockAPI(BaseInterfaceModel):
An abstract class representing a block and its attributes.
"""

# NOTE: All fields in this class (and it's subclasses) should not be `Optional`
# except the edge cases noted below

num_transactions: int = 0
hash: Optional[Any] = None # NOTE: pending block does not have a hash
number: Optional[int] = None
number: Optional[int] = None # NOTE: pending block does not have a number
parent_hash: Any = Field(
EMPTY_BYTES32, alias="parentHash"
) # NOTE: genesis block has no parent hash
Expand Down
2 changes: 1 addition & 1 deletion src/ape/api/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def perform_query(self, query: QueryType) -> Iterator:
Iterator
"""

def update_cache(self, query: QueryType, result: Iterator):
def update_cache(self, query: QueryType, result: Iterator[BaseInterfaceModel]):
"""
Allows a query plugin the chance to update any cache using the results obtained
from other query plugins. Defaults to doing nothing, override to store cache data.
Expand Down
5 changes: 2 additions & 3 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,11 @@ def query(
)

blocks = self.query_manager.query(query, engine_to_use=engine_to_use)
data = map(lambda val: val.dict(by_alias=False), blocks)

# NOTE: Allow any columns from ecosystem's BlockAPI class
# TODO: fetch the block fields from EcosystemAPI
columns = validate_and_expand_columns(columns, list(self.head.__fields__)) # type: ignore
return pd.DataFrame(columns=columns, data=data)
blocks = map(lambda val: val.dict(by_alias=False), blocks) # type: ignore
return pd.DataFrame(columns=columns, data=blocks)

def range(
self,
Expand Down
47 changes: 31 additions & 16 deletions src/ape/managers/query.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from itertools import tee
from typing import Dict, Iterator, Optional

from ape.api import QueryAPI, QueryType
from ape.api.query import BlockQuery, BlockTransactionQuery, ContractEventQuery
from ape.api import QueryAPI, QueryType, TransactionAPI
from ape.api.query import BaseInterfaceModel, BlockQuery, BlockTransactionQuery, ContractEventQuery
from ape.contracts.base import ContractLog, LogFilter
from ape.exceptions import QueryEngineError
from ape.logging import logger
from ape.plugins import clean_plugin_name
from ape.utils import ManagerAccessMixin, cached_property, singledispatchmethod

Expand All @@ -26,13 +28,13 @@ def estimate_block_query(self, query: BlockQuery) -> Optional[int]:

@estimate_query.register
def estimate_block_transaction_query(self, query: BlockTransactionQuery) -> int:

return 100
# NOTE: Very loose estimate of 1000ms per block for this query.
return self.provider.get_block(query.block_id).num_transactions * 100

@estimate_query.register
def estimate_contract_events_query(self, query: ContractEventQuery) -> int:
# NOTE: Very loose estimate of 100ms per block for this query.
return 100
return (query.stop_block - query.start_block) * 100

@singledispatchmethod
def perform_query(self, query: QueryType) -> Iterator: # type: ignore
Expand All @@ -43,12 +45,14 @@ def perform_block_query(self, query: BlockQuery) -> Iterator:
return map(
self.provider.get_block,
# NOTE: the range stop block is a non-inclusive stop.
# Where as the query method is an inclusive stop.
# Where the query method is an inclusive stop.
range(query.start_block, query.stop_block + 1, query.step),
)

@perform_query.register
def perform_block_transaction_query(self, query: BlockTransactionQuery) -> Iterator:
def perform_block_transaction_query(
self, query: BlockTransactionQuery
) -> Iterator[TransactionAPI]:
return self.provider.get_transactions_by_block(query.block_id)

@perform_query.register
Expand Down Expand Up @@ -91,30 +95,35 @@ def engines(self) -> Dict[str, QueryAPI]:

engines: Dict[str, QueryAPI] = {"__default__": DefaultQueryProvider()}

for plugin_name, (engine_class,) in self.plugin_manager.query_engines:
for plugin_name, engine_class in self.plugin_manager.query_engines:
engine_name = clean_plugin_name(plugin_name)
engines[engine_name] = engine_class()
engines[engine_name] = engine_class() # type: ignore
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved

return engines

def query(self, query: QueryType, engine_to_use: Optional[str] = None) -> Iterator[QueryAPI]:
def query(
self,
query: QueryType,
engine_to_use: Optional[str] = None,
) -> Iterator[BaseInterfaceModel]:
"""
Args:
query (``QueryType``): The type of query to execute
engine_to_use (Optional[str]): Short-circuit selection logic using
a specific engine. Defaults to None.
a specific engine. Defaults is set by performance-based selection logic.

johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
Raises: :class:`~ape.exceptions.QueryEngineError`: When given an
invalid or inaccessible ``engine_to_use`` value.

johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
Returns:
Iterator[QueryAPI]
Iterator[BaseInterfaceModel]
"""

if engine_to_use:
if engine_to_use not in self.engines:
raise QueryEngineError(f"Query engine `{engine_to_use}` not found.")

engine = self.engines[engine_to_use]
selected_engine = self.engines[engine_to_use]

else:
# Get heuristics from all the query engines to perform this query
Expand All @@ -126,15 +135,21 @@ def query(self, query: QueryType, engine_to_use: Optional[str] = None) -> Iterat
try:
# Find the "best" engine to perform the query
# NOTE: Sorted by fastest time heuristic
engine, _ = min(valid_estimates, key=lambda qe: qe[1]) # type: ignore
selected_engine, _ = min(valid_estimates, key=lambda qe: qe[1]) # type: ignore

except ValueError as e:
raise QueryEngineError("No query engines are available.") from e

# Go fetch the result from the engine
result = engine.perform_query(query)
result = selected_engine.perform_query(query)

# Update any caches
for engine in self.engines.values():
engine.update_cache(query, result)
if not isinstance(engine, selected_engine.__class__):
result, cache_data = tee(result)
try:
engine.update_cache(query, cache_data)
except QueryEngineError as err:
logger.error(str(err))

return result
18 changes: 18 additions & 0 deletions src/ape_cache/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from ape import plugins
from ape.api import PluginConfig

from .query import CacheQueryProvider


class CacheConfig(PluginConfig):
size: int = 1024**3 # 1gb
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved


@plugins.register(plugins.Config)
def config_class():
return CacheConfig


@plugins.register(plugins.QueryPlugin)
def query_engines():
return CacheQueryProvider
82 changes: 82 additions & 0 deletions src/ape_cache/_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import click
import pandas as pd

from ape import networks
from ape.cli import NetworkBoundCommand, network_option
from ape.logging import logger
from ape.utils import ManagerAccessMixin
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved


def get_engine():
return ManagerAccessMixin.query_manager.engines["cache"]


@click.group(short_help="Query from caching database")
def cli():
"""
Manage query caching database (beta).
"""


@cli.command(short_help="Initialize a new cache database")
@network_option(required=True)
def init(network):
"""
Initializes an SQLite database and creates a file to store data
from the provider.

Note that ape cannot store local data in this database. You have to
give an ecosystem name and a network name to initialize the database.
"""

provider = networks.get_provider_from_choice(network)
ecosystem_name = provider.network.ecosystem.name
network_name = provider.network.name

get_engine().init_database(ecosystem_name, network_name)
logger.success(f"Caching database initialized for {ecosystem_name}:{network_name}.")


@cli.command(
cls=NetworkBoundCommand,
short_help="Call and print SQL statement to the cache database",
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
)
@network_option()
antazoey marked this conversation as resolved.
Show resolved Hide resolved
@click.argument("query_str")
def query(query_str, network):
"""
Allows for a query of the database from an SQL statement.

Note that without an SQL statement, this method will not return
any data from the caching database.

Also note that an ecosystem name and a network name are required
to make the correct connection to the database.
"""

with get_engine().database_connection as conn:
results = conn.execute(query_str).fetchall()
if results:
click.echo(pd.DataFrame(results))


@cli.command(short_help="Purges entire database")
@network_option(required=True)
def purge(network):
"""
Purges data from the selected database instance.

Note that this is a destructive purge, and will remove the database file from disk.
If you want to store data in the caching system, you will have to
re-initiate the database following a purge.

Note that an ecosystem name and network name are required to
purge the database of choice.
"""

provider = networks.get_provider_from_choice(network)
ecosystem_name = provider.network.ecosystem.name
network_name = provider.network.name

get_engine().purge_database(ecosystem_name, network_name)
logger.success(f"Caching database purged for {ecosystem_name}:{network_name}.")
17 changes: 17 additions & 0 deletions src/ape_cache/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Any

from sqlalchemy.ext.declarative import as_declarative, declared_attr


@as_declarative()
class Base:
"""
Base class to generate ``__tablename__`` automatically
johnson2427 marked this conversation as resolved.
Show resolved Hide resolved
"""

id: Any
__name__: str

@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower()