Code Monkey home page Code Monkey logo

zbus-old's People

Contributors

alxandr avatar benferse avatar bilelmoussaoui avatar carlosmn avatar danieldg avatar elinorbgr avatar elmarco avatar federicomenaquintero avatar glenlowland avatar guillaumegomez avatar ids1024 avatar jplatte avatar lucab avatar maxverevkin avatar mominul avatar ollpu avatar r-ml avatar refi64 avatar rosslannen avatar swsnr avatar tim-seoss avatar tmuehlbacher avatar travier avatar turbocool3r avatar tyrylu avatar valpackett avatar vanadiae avatar xou816 avatar zeenix avatar zhangyuannie avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

zbus-old's Issues

Fails to serialize a struct with HashMap

In GitLab by @elmarco on May 7, 2020, 15:22

The following is bugging the serializer:

    #[test]
    fn struct_with_hashmap_of_value() {
        use crate as zvariant;
        use serde::Serialize;

        let mut hm = HashMap::new();
        hm.insert("key".into(), Value::from(55));

        #[derive(Type, Serialize)]
        struct Foo {
            hmap: HashMap<String, Value<'static>>,
        }

        let foo = Foo { hmap: hm };

        let ctxt = Context::<LE>::new_dbus(0);
        to_bytes(ctxt, &foo).unwrap();
    }

thread 'tests::struct_with_hashmap_of_value' panicked at 'called Result::unwrap()on anErrvalue: Message("invalid type: charactera, expected s, g, oorv")', zvariant/src/lib.rs:738:9

When the struct has another field inside, the error is bit different. That might be another related issue.

zb: Make Connection fields private

The following discussion from !60 should be addressed:

  • @zeenix started a discussion:

    Hmm.. we should make these fields private and provide getters. From user's POV unique_name must always be correctly set after Connection is created.

Proxy API discussion

In GitLab by @elmarco on May 15, 2020, 01:03

Before I do some cleanups and propose the proxy API, I could take some feedback. Please focus on externally visible API, all the inner stuff we can evolve later. Also, I focus exclusively on owned version for now, because it is more convenient to use and that's what I need first. I think we can derive a borrowed / callback-based version in a later iteration. It is also missing signals, which will have to be registered through some kind of dispatcher, or other API which are to be defined.

Does the overall Proxy API look ok? This is mostly an internal API for the derive macro to use, but it will be public, and may be used directly if prefered (not a good idea I think).

https://gitlab.freedesktop.org/elmarco/zbus/-/blob/zbus-wip/zbus/examples/server/proxy.rs

pub type ProxyResult<T> = std::result::Result<T, ProxyError>;

impl<'c> Proxy<'c> {
    pub fn new(
        conn: &'c Connection,
        destination: &str,
        path: &str,
        interface: &str,
    ) -> ProxyResult<Self> 

    pub fn introspect(&self) -> ProxyResult<String> 

    pub fn try_get<T>(&self, property_name: &str) -> ProxyResult<T>
    where
        T: TryFrom<OwnedValue>

    pub fn try_set<'a, T: 'a>(&self, property_name: &str, value: T) -> ProxyResult<()>
    where
        T: Into<Value<'a>>

    pub fn call<B, R>(&self, method_name: &str, body: &B) -> ProxyResult<R>
    where
        B: serde::ser::Serialize + zvariant::Type,
        R: serde::de::DeserializeOwned + zvariant::Type
}
  • should that be on top-level namespace, or ::proxy or else?
  • should it use a seperate Error/Result or reuse Connection's?

For the macro/derive part, I have looked at various DBus APIs (fdo, systemd and polkit + my own experience). A good example usage I propose is: https://gitlab.freedesktop.org/elmarco/zbus/-/blob/zbus-wip/zbus/examples/server/polkit1.rs#L299

#[DBusProxy(
    interface = "org.freedesktop.PolicyKit1.Authority",
    default_service = "org.freedesktop.PolicyKit1",
    default_path = "/org/freedesktop/PolicyKit1/Authority"
)]
trait Authority {
    fn CheckAuthorization(
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> AuthorizationResult;

    #[DBusProxy(property)]
    fn BackendFeatures() -> AuthorityFeatures;

    /// some other example for getter/setters.
    #[DBusProxy(property)]
    fn KExecWatchdogUSec() -> u64;
    /// A property method that has input args is a setter. By default, it will strip the Set from the name for the property name
    #[DBusProxy(property)]
    fn SetKExecWatchdogUSec(value: u64);

}

The actual derived impl looks something like:

impl Authority {
    // constructors:
    fn new(c: &Connection) -> Self;
    fn new_for(c: &Connection, dest: &str, path &str) -> Self;

    // method call, &self is appended, ProxyResult wraps the returned value 
    fn CheckAuthorization(
        &self,
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> ProxyResult<AuthorizationResult> {
        self.call("CheckAuthorization", &(subject, action_id,...))
    }

    // same in snake_case
    fn check_authorization(
        &self,
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> ProxyResult<AuthorizationResult>;

    fn BackendFeatures(&self) -> ProxyResult<AuthorityFeatures> {
       self.try_get("BackendFeatures")
    }
    fn backend_features(&self) -> ProxyResult<AuthorityFeatures>

    fn SetKExecWatchdogUSec(value: u64) -> ProxyResult<()> {
        self.try_set("KExecWatchdogUSec", &value)
    }
    fn set_kexec_watchdog_usec(value: u64) -> ProxyResult<()>; // actually, my snake-case function is buggy atm :)

In the trait,

  • should it have the &self explicitly
  • similarly, should it have the ProxyResult explicetly too?
    (both would be quite verbose and repetitive, so I decided not to have them by default, and only reflect DBus API)
  • again, to better reflect the DBus API, I prefered to use the casing used over DBus by default when declaring the trait, and let the macro derive the rest. Should we instead have snake_case method converted to CamelCase? (with exceptions being edited with additional attributes?)

The traits can be automatically generated from introspection XML, only higher level types (structs, flags) are done manually, since the schema doesn't expose such details.

Perhaps a trait is not the most appropriate to declare the DBus API, but then what should it be? Some adhoc rust-derived description language?

Any other comment?

Value from/to tuples direct conversion

We already provide conversion from tuples to Structures and from Structure to Value but it'd be great if we provide a direct conversion from/to tuples from/to Value as well.

RFC: serializing structs as flat data

In GitLab by @elmarco on Apr 29, 2020, 17:16

The following struct is serialized as "(s...)":

#[derive(Type)]
struct TestStruct {
    name: String,
    ...
}

I think it could be useful if zvariant/derive would have the option to serialize it as "s..." instead. Or is it already possible?

Provide Serde abstractions for Variant API

In GitLab by @programmerjake on Nov 27, 2019, 01:58

serde has the benefit of a well thought out API with support for arbitrary protocols, as well as being the defacto standard for Rust. Even if you don't end up using it, emulating the serialization/de-serialization traits would probably be quite helpful. See serde_cbor for a simple example binary data format.

Combo traits for easing trait bounds

We should provide the following (empty) traits to facilitate trait bounds:

  • Serialize: serde::ser::Serialize + Type
  • Deserialize trait: serde::ser::Deserialize + Type

Optimize `serialized_size` function

Current implementation creates bytes representation of the entire value to be serialized. I think we can avoid that by create a separate serializer just for size and just calculate the size in there.

[Feature] Zero-copy, zero-allocation decoding of zvariant

In GitLab by @SDavidoff on Feb 8, 2020, 19:18

The layout of D-bus messages is very friendly towards zero-copy decoding: since everything is already properly aligned, you can access the data in memory directly instead of allocating and copying it to the outside.

This can be done without major changes to the API since this is only relevant for strings and arrays, not primitive types. All you'd need to add is converting zvariant::Array into a slice instead of a Vec and zvariant::String into an &str.

For &str there is already a function to perform the checks and convert: https://doc.rust-lang.org/std/str/fn.from_utf8.html

All array types except bool are covered by try_cast() methods from bytemuck crate: https://docs.rs/bytemuck/1.2.0/bytemuck/
&[bool] has additional validity invariants (only values of 0 or 1 are allowed) and will require a traversal for validation, and a manual unsafe cast.

The only tricky part is that you'd need to align the entirety of zvariant heap allocation to the largest possible alignment requirement (8 bytes for u64) so you might have to add up to 7 padding bytes before the zvariant itself even begins.

Fails to serialize a struct with HashMap

In GitLab by @elmarco on May 7, 2020, 15:22

The following is bugging the serializer:

    #[test]
    fn struct_with_hashmap_of_value() {
        use crate as zvariant;
        use serde::Serialize;

        let mut hm = HashMap::new();
        hm.insert("key".into(), Value::from(55));

        #[derive(Type, Serialize)]
        struct Foo {
            hmap: HashMap<String, Value<'static>>,
        }

        let foo = Foo { hmap: hm };

        let ctxt = Context::<LE>::new_dbus(0);
        to_bytes(ctxt, &foo).unwrap();
    }

thread 'tests::struct_with_hashmap_of_value' panicked at 'called Result::unwrap()on anErrvalue: Message("invalid type: charactera, expected s, g, oorv")', zvariant/src/lib.rs:738:9

When the struct has another field inside, the error is bit different. That might be another related issue.

Needless dependency on byteorder crate

In GitLab by @SDavidoff on Feb 8, 2020, 18:13

The dependency on byteorder crate can be easily avoided, since all the relevant functions are already provided by the standard library (including handling endianness).

Example of stdlib replacement for read_ methods can be found here: https://github.com/cbreeden/fxhash/pull/13/files

Writes can be done through to_ne_bytes: https://doc.rust-lang.org/stable/std/primitive.u32.html#method.to_ne_bytes

The change should be quite trivial, but sadly I am under legal restrictions and cannot contribute to this repository.

Inefficient array decoding

The current Decode::decode implementations decodes all the elements upfront, which is very inefficient. It's not a problem in most cases in the context of D-Bus but GVariant was designed to avoid exactly this kind of inefficiency since it's also meant to contain large sets of data.

We probably want an additional API that can deserialize only specific range from an encoded array. It's OK if we can only make it work for GVariant format.

GVariant format support

As we've always planned. The spec is here. For primitive types it's exactly the same so it shouldn't be very hard. For container types and strings, the most important difference is that GVariant keeps metadata at the end, not beginning.

We should test against glib's API to ensure compatibility and maybe also compare the performance diff, if any. Some very WIP work exists in this branch: https://gitlab.freedesktop.org/zeenix/zbus/-/tree/wip/gvariant

Checklist:

  • Ensure we always encode and decode in the right byte-order.
  • Deserializer code for container types must deserialize from child value from their respective slices only. This likely means separate Deserializer instance(s) for child values.
  • Deserializer assumes byte slice contains only the type currently being decoded, for basic types, this is only relevant to string (including signature & object path) deserialization.
  • Support for Maybe type, which should map nicely to Option<T: Type>
  • (OPTIONAL) It would be great if we could also add integration tests against Ostree.

Optimize `serialized_size` function

Current implementation creates bytes representation of the entire value to be serialized. I think we can avoid that by create a separate serializer just for size and just calculate the size in there.

System bus connections

In GitLab by @tim-seoss on Apr 14, 2020, 16:24

Hello,

I know nearly nothing about dbus, but I'd like to write a small tool in Rust to pull out this sort of data from ModemManager:

dbus-send --system --print-reply --dest=org.freedesktop.ModemManager1 /org/freedesktop/ModemManager1/Modem/0 org.freedesktop.DBus.Properties.Get string:org.freedesktop.ModemManager1.Modem.Signal string:Lte
method return time=1586873244.543342 sender=:1.7818 -> destination=:1.7921 serial=10053 reply_serial=2
   variant       array [
         dict entry(
            string "rssi"
            variant                double -46
         )
         dict entry(
            string "rsrp"
            variant                double -76
         )
         dict entry(
            string "rsrq"
            variant                double -11
         )
         dict entry(
            string "snr"
            variant                double 8.4
         )
      ]

... so thought I would look at zbus to accomplish this. I noticed that only the session bus is currently supported, so thought I'd hack on it a bit to see if I could get system connections working. As a PoC I changed the connection socket path to /var/run/dbus/system_bus_socket and tried to print out the result of: GetMachineId.

Whilst the debug output seems to start OK (debug output shows that Hello succeeds, and the system bus name is printed out), the call_method to GetMachineID (which succeeds against the session bus) returns an Err(Variant(IncorrectType)) on the system bus.

As a relative Rust and Dbus newbie, should I carry on hacking on this, and if-so, any tips?

Provide Serde abstractions for Variant API

In GitLab by @programmerjake on Nov 27, 2019, 01:58

serde has the benefit of a well thought out API with support for arbitrary protocols, as well as being the defacto standard for Rust. Even if you don't end up using it, emulating the serialization/de-serialization traits would probably be quite helpful. See serde_cbor for a simple example binary data format.

[Feature] Zero-copy, zero-allocation decoding of zvariant

In GitLab by @SDavidoff on Feb 8, 2020, 19:18

The layout of D-bus messages is very friendly towards zero-copy decoding: since everything is already properly aligned, you can access the data in memory directly instead of allocating and copying it to the outside.

This can be done without major changes to the API since this is only relevant for strings and arrays, not primitive types. All you'd need to add is converting zvariant::Array into a slice instead of a Vec and zvariant::String into an &str.

For &str there is already a function to perform the checks and convert: https://doc.rust-lang.org/std/str/fn.from_utf8.html

All array types except bool are covered by try_cast() methods from bytemuck crate: https://docs.rs/bytemuck/1.2.0/bytemuck/
&[bool] has additional validity invariants (only values of 0 or 1 are allowed) and will require a traversal for validation, and a manual unsafe cast.

The only tricky part is that you'd need to align the entirety of zvariant heap allocation to the largest possible alignment requirement (8 bytes for u64) so you might have to add up to 7 padding bytes before the zvariant itself even begins.

Proxy API discussion

In GitLab by @elmarco on May 15, 2020, 01:03

Before I do some cleanups and propose the proxy API, I could take some feedback. Please focus on externally visible API, all the inner stuff we can evolve later. Also, I focus exclusively on owned version for now, because it is more convenient to use and that's what I need first. I think we can derive a borrowed / callback-based version in a later iteration. It is also missing signals, which will have to be registered through some kind of dispatcher, or other API which are to be defined.

Does the overall Proxy API look ok? This is mostly an internal API for the derive macro to use, but it will be public, and may be used directly if prefered (not a good idea I think).

https://gitlab.freedesktop.org/elmarco/zbus/-/blob/zbus-wip/zbus/examples/server/proxy.rs

pub type ProxyResult<T> = std::result::Result<T, ProxyError>;

impl<'c> Proxy<'c> {
    pub fn new(
        conn: &'c Connection,
        destination: &str,
        path: &str,
        interface: &str,
    ) -> ProxyResult<Self> 

    pub fn introspect(&self) -> ProxyResult<String> 

    pub fn try_get<T>(&self, property_name: &str) -> ProxyResult<T>
    where
        T: TryFrom<OwnedValue>

    pub fn try_set<'a, T: 'a>(&self, property_name: &str, value: T) -> ProxyResult<()>
    where
        T: Into<Value<'a>>

    pub fn call<B, R>(&self, method_name: &str, body: &B) -> ProxyResult<R>
    where
        B: serde::ser::Serialize + zvariant::Type,
        R: serde::de::DeserializeOwned + zvariant::Type
}
  • should that be on top-level namespace, or ::proxy or else?
  • should it use a seperate Error/Result or reuse Connection's?

For the macro/derive part, I have looked at various DBus APIs (fdo, systemd and polkit + my own experience). A good example usage I propose is: https://gitlab.freedesktop.org/elmarco/zbus/-/blob/zbus-wip/zbus/examples/server/polkit1.rs#L299

#[DBusProxy(
    interface = "org.freedesktop.PolicyKit1.Authority",
    default_service = "org.freedesktop.PolicyKit1",
    default_path = "/org/freedesktop/PolicyKit1/Authority"
)]
trait Authority {
    fn CheckAuthorization(
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> AuthorizationResult;

    #[DBusProxy(property)]
    fn BackendFeatures() -> AuthorityFeatures;

    /// some other example for getter/setters.
    #[DBusProxy(property)]
    fn KExecWatchdogUSec() -> u64;
    /// A property method that has input args is a setter. By default, it will strip the Set from the name for the property name
    #[DBusProxy(property)]
    fn SetKExecWatchdogUSec(value: u64);

}

The actual derived impl looks something like:

impl Authority {
    // constructors:
    fn new(c: &Connection) -> Self;
    fn new_for(c: &Connection, dest: &str, path &str) -> Self;

    // method call, &self is appended, ProxyResult wraps the returned value 
    fn CheckAuthorization(
        &self,
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> ProxyResult<AuthorizationResult> {
        self.call("CheckAuthorization", &(subject, action_id,...))
    }

    // same in snake_case
    fn check_authorization(
        &self,
        subject: &Subject,
        action_id: &str,
        details: std::collections::HashMap<&str, &str>,
        flags: CheckAuthorizationFlags,
        cancellation_id: &str,
    ) -> ProxyResult<AuthorizationResult>;

    fn BackendFeatures(&self) -> ProxyResult<AuthorityFeatures> {
       self.try_get("BackendFeatures")
    }
    fn backend_features(&self) -> ProxyResult<AuthorityFeatures>

    fn SetKExecWatchdogUSec(value: u64) -> ProxyResult<()> {
        self.try_set("KExecWatchdogUSec", &value)
    }
    fn set_kexec_watchdog_usec(value: u64) -> ProxyResult<()>; // actually, my snake-case function is buggy atm :)

In the trait,

  • should it have the &self explicitly
  • similarly, should it have the ProxyResult explicetly too?
    (both would be quite verbose and repetitive, so I decided not to have them by default, and only reflect DBus API)
  • again, to better reflect the DBus API, I prefered to use the casing used over DBus by default when declaring the trait, and let the macro derive the rest. Should we instead have snake_case method converted to CamelCase? (with exceptions being edited with additional attributes?)

The traits can be automatically generated from introspection XML, only higher level types (structs, flags) are done manually, since the schema doesn't expose such details.

Perhaps a trait is not the most appropriate to declare the DBus API, but then what should it be? Some adhoc rust-derived description language?

Any other comment?

Needless dependency on byteorder crate

In GitLab by @SDavidoff on Feb 8, 2020, 18:13

The dependency on byteorder crate can be easily avoided, since all the relevant functions are already provided by the standard library (including handling endianness).

Example of stdlib replacement for read_ methods can be found here: https://github.com/cbreeden/fxhash/pull/13/files

Writes can be done through to_ne_bytes: https://doc.rust-lang.org/stable/std/primitive.u32.html#method.to_ne_bytes

The change should be quite trivial, but sadly I am under legal restrictions and cannot contribute to this repository.

zb: Make Connection fields private

The following discussion from !60 should be addressed:

  • @zeenix started a discussion:

    Hmm.. we should make these fields private and provide getters. From user's POV unique_name must always be correctly set after Connection is created.

RFC: serializing structs as flat data

In GitLab by @elmarco on Apr 29, 2020, 17:16

The following struct is serialized as "(s...)":

#[derive(Type)]
struct TestStruct {
    name: String,
    ...
}

I think it could be useful if zvariant/derive would have the option to serialize it as "s..." instead. Or is it already possible?

no-std support

Would be nice if that's possible. No 'no-alloc' though and we'll need to use alloc crate. See how it's done in serde.

Don't forget to update the docs, when this is added.

System bus connections

In GitLab by @tim-seoss on Apr 14, 2020, 16:24

Hello,

I know nearly nothing about dbus, but I'd like to write a small tool in Rust to pull out this sort of data from ModemManager:

dbus-send --system --print-reply --dest=org.freedesktop.ModemManager1 /org/freedesktop/ModemManager1/Modem/0 org.freedesktop.DBus.Properties.Get string:org.freedesktop.ModemManager1.Modem.Signal string:Lte
method return time=1586873244.543342 sender=:1.7818 -> destination=:1.7921 serial=10053 reply_serial=2
   variant       array [
         dict entry(
            string "rssi"
            variant                double -46
         )
         dict entry(
            string "rsrp"
            variant                double -76
         )
         dict entry(
            string "rsrq"
            variant                double -11
         )
         dict entry(
            string "snr"
            variant                double 8.4
         )
      ]

... so thought I would look at zbus to accomplish this. I noticed that only the session bus is currently supported, so thought I'd hack on it a bit to see if I could get system connections working. As a PoC I changed the connection socket path to /var/run/dbus/system_bus_socket and tried to print out the result of: GetMachineId.

Whilst the debug output seems to start OK (debug output shows that Hello succeeds, and the system bus name is printed out), the call_method to GetMachineID (which succeeds against the session bus) returns an Err(Variant(IncorrectType)) on the system bus.

As a relative Rust and Dbus newbie, should I carry on hacking on this, and if-so, any tips?

Combo traits for easing trait bounds

We should provide the following (empty) traits to facilitate trait bounds:

  • Serialize: serde::ser::Serialize + Type
  • Deserialize trait: serde::ser::Deserialize + Type

GVariant format support

As we've always planned. The spec is here. For primitive types it's exactly the same so it shouldn't be very hard. For container types and strings, the most important difference is that GVariant keeps metadata at the end, not beginning.

We should test against glib's API to ensure compatibility and maybe also compare the performance diff, if any. Some very WIP work exists in this branch: https://gitlab.freedesktop.org/zeenix/zbus/-/tree/wip/gvariant

Checklist:

  • Ensure we always encode and decode in the right byte-order.
  • Deserializer code for container types must deserialize from child value from their respective slices only. This likely means separate Deserializer instance(s) for child values.
  • Deserializer assumes byte slice contains only the type currently being decoded, for basic types, this is only relevant to string (including signature & object path) deserialization.
  • Support for Maybe type, which should map nicely to Option<T: Type>
  • (OPTIONAL) It would be great if we could also add integration tests against Ostree.

Create a custom CI image

At the moment, it'll save us the following steps in each stage run:

apt-get --allow-unauthenticated update -yq
apt-get install -o dir::cache::archives="$APT_CACHE_DIR" -y dbus

Maybe use gitlab's template support?

Create a custom CI image

At the moment, it'll save us the following steps in each stage run:

apt-get --allow-unauthenticated update -yq
apt-get install -o dir::cache::archives="$APT_CACHE_DIR" -y dbus

Maybe use gitlab's template support?

no-std support

Would be nice if that's possible. No 'no-alloc' though and we'll need to use alloc crate. See how it's done in serde.

Don't forget to update the docs, when this is added.

Inefficient array decoding

The current Decode::decode implementations decodes all the elements upfront, which is very inefficient. It's not a problem in most cases in the context of D-Bus but GVariant was designed to avoid exactly this kind of inefficiency since it's also meant to contain large sets of data.

We probably want an additional API that can deserialize only specific range from an encoded array. It's OK if we can only make it work for GVariant format.

Value from/to tuples direct conversion

We already provide conversion from tuples to Structures and from Structure to Value but it'd be great if we provide a direct conversion from/to tuples from/to Value as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.