demergent-labs / kybra Goto Github PK
View Code? Open in Web Editor NEWPython CDK for the Internet Computer
License: MIT License
Python CDK for the Internet Computer
License: MIT License
In the worst case we'll have to write our own tool. But there are some standalone Python app bundlers that might also do the trick, for example https://py2app.readthedocs.io/en/latest/
All we need is to get all imported modules into one directory so that RustPython's py_freeze works
Right now in our Kybra/Azle compilers, when we come across any kind of inline record in records or enums, we create a new rust struct with a random name, and have to do some extra plumbing to get things to work well. This causes our Candid files to be messier than they could be, and the extra plumbing in our compilers is a cost as well.
If you embraced the enum named fields syntax, we might be able to reduce that plumbing for inline records found within enums. See here for what the syntax looks like: https://stackoverflow.com/questions/52134531/how-to-get-access-to-enum-variant-unnamed-field
We might want to start this project out with Rust as the base language. See here: demergent-labs/azle#151
How to represent records, variants, arrays, and other non-primitives in Python typing, how to represent all types (make an issue for each showing how they should look). I just found this awesome Rust/Python types chart: https://pyo3.rs/v0.13.2/conversions/tables.html
# str should be an alias to text
text = str
blob = bytes
# or
blob = bytearray
nat = int
nat64 = int
nat32 = int
nat16 = int
nat8 = int
# int is builtin
int64 = int
int32 = int
int16 = int
int8 = int
float32 = float
float64 = float
# bool is builtin
# None should be an alias to null
null = None
T = TypeVar('T')
list[T]
# example
@query
def first_list(list_a: list[int], list_b: list[nat]) -> list[int]:
return list_a
T = TypeVar('T')
opt = Optional[T]
# example
@query
def first_opt(opt_a: opt[str], opt_b: opt[bool]) -> opt[str]:
return opt_a
# kybra module
record = TypedDict
# example
from kybra import record
MyRecord = record('MyRecord', {
'prop1': str,
'prop2': int
})
# or
class MyRecord(record):
prop1: str,
prop2: int
# or ideally I think we want this, behaving like a data class or TypedDict
@record
class MyRecord:
prop1: str,
prop2: int
my_tuple = tuple[str, str]
# TODO consider what to do after figuring out records
# TODO we might want to see what ic-py is doing
# We might just want to do some weird inheritance thing with TypedDict, like Variant
# We might want to do that for records and variants
# kybra module
variant = TypedDict
# example
from kybra import variant
MyVariant = variant('MyVariant', {
'prop1': str,
'prop2': int
})
# or
class MyVariant(variant):
prop1: str,
prop2: int
# or ideally I think we want this, behaving like a data class or TypedDict
@variant
class MyVariant:
prop1: str,
prop2: int
# kybra module
def Func(callable: Callable) -> Type[tuple[int, str]]:
return type((0, ''))
# example
from kybra import Func
MyFunc: TypeAlias = Func(Query[[], str])
Use the Principal
type from https://github.com/rocklabs-io/ic-py
from ic.principal import Principal
p = Principal() # default is management canister id `aaaaa-aa`
p1 = Principal(bytes=b'') # create an instance from bytes
p2 = Principal.anonymous() # create anonymous principal
p3 = Principal.self_authenticating(pubkey) # create a principal from public key
p4 = Principal.from_str('aaaaa-aa') # create an instance from string
p5 = Principal.from_hex('xxx') # create an instance from hex
from typing import Any
reserved = Any
from typing import NoReturn
empty = NoReturn
from kybra import canister, Query, Update, Oneway
@canister
class MyCanister:
read: Query[[int, str], str]
write: Update[[], str]
oneway: Oneway[[], None]
It would be awesome to allow canister indexing tools to discover which CDK canisters were written in. This should be easily identifiable by doing something like the following in the Candid files:
type CDK = { kybra };
Right now the build process keeps progressing even if there are for example Rust errors
These are blockers for Kybra that we do not have direct control over. We should prioritize these when discussing with DFINITY, the community, and other projects:
dfx kybra [command]
and dfx.json
)See this forum thread here: https://forum.dfinity.org/t/wasm-module-contains-a-function-that-is-too-complex/15407
We've got to find a way to get around this issue or we're stuck.
Importing the entire stdlib with RustPython, including the Rust-implemented stdlib and the frozen stdlib, creates a binary that is far too large to deploy to the IC. After a lot of experimentation, we have decided to move on for now. We will only include the Rust-implemented stdlib.
There are basically two solutions that we find the most promising so far. One is for RustPython to greatly decrease the size of frozen modules, and the other is for DFINITY to increase the Wasm binary limit:
Suggest the following change to Readme.md.
Change from:
"Your main.py file should look like this":
Change to:
"Your main.py file should be save in ./src and look like this:"
Preferred order: query methods, update methods, records, variants, system methods, ic object
The first step here is to get RustPython to compile to wasm32-unknown-unknown and deploy it to a local canister. I've attempted this unsuccessfully but didn't spend too much time on it. See here: RustPython/RustPython#2378
These are stdlib modules that create a binary size far too large (like 20-30mb uncompressed):
http://www.elebihan.com/posts/using-rust-with-buildroot-pre-built-binaries.html
For Kybra and Azle we'll eventually want to ship some kind of cargo/rustc installation script that can just pull down the prebuilt binaries. This would remove the need for the user to install Rust on their system.
Right now our vectors are all just lists, but especially for Vec we will probably want to use a more efficient conversion, and ensure that we convert to bytes.
One possible way to optimize in the future is to freeze or compile the Python source code we are to execute into Python bytecode. This would save the canister from that whole compilation step I would imagine.
For example, we want our canisters to be interacted with from websites like icscan.io. To achieve this, I think there are two paths. One is to have the Candid data stored automatically in the Wasm binary. Another I believe is the __get_temp_hack thing. Kybra already has __get_temp_hack. We may want to also or instead of that do the Candid service data stored in the binary.
Whatever we do, we should ensure the canisters are discoverable.
We need to elegantly import external libraries
This is architecture that needs to be put in place before the CDK ACT framework and AST manipulations can come in full force:
It might be better to just use the builtin venv
: https://docs.python.org/3/library/venv.html
# installation
curl https://pyenv.run | bash
# install specific version
~/.pyenv/bin/pyenv install 3.10.7
# binary path
~/.pyenv/versions/3.10.7/bin/python3 --version
Apparently distutils is deprecated as of Python 3.10: https://docs.python.org/3/whatsnew/3.10.html
It looks like RustPython does not currently have garbage collection, see these issues:
This should work very similarly to the TypeScript -> Candid compiler found in Azle: https://github.com/demergent-labs/azle/tree/main/src/compiler/typescript_to_candid
This might be a fundamental limitation currently set on the IC. I'm not sure I really have the ability to change this, as RustPython is probably the source of all of these functions.
Until the IC increases its Wasm binary size limit, importing typing is just too much...I think because it imports all of the other libs to do its types? I am not sure...for now we might need to just pull from it what we need, and not allow the user to ever import it directly unfortunately. We should have the user import the types they need from kybra for now
It seems like when I try to do from typing import Any
and use the Any
type, then my code breaks. This might be happening with other types as well, I'm not sure what's going on. I've opened an issue with RustPython: RustPython/RustPython#4173
An initial implementation can probably be very similar to Azle's and use JSON.
Close out this issue as well: demergent-labs/azle#654
Really think about if this type of default stable storage implementation will be worth it to the developer.
hello.
It's a great project and I've been looking forward to it being available in alpha.
When I tried to deploy, I got the following error.
I'm using Apple Silicon (M1 chip).
$dfx -V
dfx 0.12.0-beta.2
$cargo -V
cargo 1.64.0 (387270bc7 2022-09-16)
$rustc -V
rustc 1.64.0 (a55dd71d5 2022-09-19)
$python -V
Python 3.9.7
Deploying all canisters.
Creating canisters...
Creating canister imports...
imports canister created with canister id: rkp4c-7iaaa-aaaaa-aaaca-cai
Building canisters...
Executing 'python -m kybra imports src/main.py src/main.did'
info: component 'rust-std' for target 'wasm32-unknown-unknown' is up to date
Updating crates.io index
Installing ic-cdk-optimizer v0.3.4
Compiling proc-macro2 v1.0.44
Compiling quote v1.0.21
Compiling unicode-ident v1.0.4
Compiling cc v1.0.73
Compiling memchr v2.5.0
Compiling version_check v0.9.4
Compiling syn v1.0.101
Compiling unicode-segmentation v1.10.0
Compiling regex-syntax v0.6.27
Compiling serde v1.0.145
Compiling autocfg v1.1.0
Compiling libc v0.2.133
Compiling serde_json v1.0.85
Compiling serde_derive v1.0.145
Compiling hashbrown v0.12.3
Compiling ryu v1.0.11
Compiling itoa v1.0.3
Compiling unicode-width v0.1.10
Compiling os_str_bytes v2.4.0
Compiling lazy_static v1.4.0
Compiling termcolor v1.1.3
Compiling bitflags v1.3.2
Compiling strsim v0.10.0
Compiling vec_map v0.8.2
Compiling humansize v1.1.1
Compiling heck v0.3.3
Compiling cmake v0.1.48
Compiling proc-macro-error-attr v1.0.4
Compiling proc-macro-error v1.0.4
Compiling textwrap v0.12.1
Compiling indexmap v1.9.1
Compiling wabt-sys v0.8.0
Compiling aho-corasick v0.7.19
Compiling regex v1.6.0
Compiling atty v0.2.14
Compiling binaryen-sys v0.12.0
Compiling clap_derive v3.0.0-beta.2
Compiling clap v3.0.0-beta.2
Compiling wabt v0.10.0
error: failed to add native library /var/folders/0h/1jsn6jwn7sz_s6zm5tk2znkw0000gn/T/cargo-installAGJQUZ/release/build/wabt-sys-21af77b6012a4d59/out/build/libwabt.a: file too small to be an archive
error: could not compile `wabt-sys` due to previous error
Looking for a solution, I ran cargo install ic-cdk-optimizer --version 0.3.4 directly in terminal and removed it from build.sh and it worked.
rustup target add wasm32-unknown-unknown
# cargo install ic-cdk-optimizer --version 0.3.4
To run kybra_generate, we need the JS part of Kybra to get a list of all the filenames that are in the bundle, and then we pass those file names into kybra_generate as arguments on the command line. This is a problem however, because importing large libraries causes this list of file names to be too large, and then we run into an ENOBUFS (105) error.
Instead, we need to, grab the list of file names from the rust side of things because that is much smaller and will get around this issue.
We are waiting for all of the number of functions and function complexity limits to be raised to sufficient levels, which should happen in dfx 0.12.0-beta.4
. We're currently on dfx 0.12.0-beta.3
. Once we have those limits increased, let's switch to ic-wasm. We want to do this because ic-cdk-optimizer
is deprecated, and it causes issues when being installed on Mac M1s.
Perhaps our ic-wasm problem will be solved now with the wasm function limit increased.
Perhaps we do not have to freeze our modules at all, maybe we can get a dynamic loader to work somehow...we could get a list of all modules required, maybe just a directory using our bundler, and then we just upload it like an asset canister. Then the files can be uploaded after deployment...no, this will be slow and complicated. I'm not sure this is the path we want to go down.
This issue entails allowing the developer to include any of their own files
Our Rust TryFrom and TryInto vm value conversion code does not have a general solution for lists of lists (vectors of vectors). This same problem exists in Azle.
async fn
is not permitted in Rust 2015 this really needs to be fixed, this error requires the project root directory to have a rustfmt.toml file, something to do with the way we're using rustfmtpip -m kybra
maybe with some argumentsFor me I had open ssl version 3.0.2 installed but I needed to install libssl-dev
sudo apt-get install libssl-dev
And that worked out ok.
Deliverable: Document this in the README.
We need to be able to from kybra import query, update
. I have this working, but I need to get it to work well with VS Code typechecking. Getting the virtualenv correct with site-packages and such should help stdlib and external libs all work well together.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.