Giter Club home page Giter Club logo

edgedb-python's Introduction

EdgeDB

Stars license discord

Quickstart   •   Website   •   Docs   •   Playground   •   Blog   •   Discord   •   Twitter



What is EdgeDB?

EdgeDB is a new kind of database
that takes the best parts of
relational databases, graph
databases, and ORMs. We call it
a graph-relational database.



🧩 Types, not tables 🧩


Schema is the foundation of your application. It should be something you can read, write, and understand.

Forget foreign keys; tabular data modeling is a relic of an older age, and it isn't compatible with modern languages. Instead, EdgeDB thinks about schema the same way you do: as object types containing properties connected by links.

type Person {
  required name: str;
}

type Movie {
  required title: str;
  multi actors: Person;
}

This example is intentionally simple, but EdgeDB supports everything you'd expect from your database: a strict type system, indexes, constraints, computed properties, stored procedures...the list goes on. Plus it gives you some shiny new features too: link properties, schema mixins, and best-in-class JSON support. Read the schema docs for details.


🌳 Objects, not rows 🌳


EdgeDB's super-powered query language EdgeQL is designed as a ground-up redesign of SQL. EdgeQL queries produce rich, structured objects, not flat lists of rows. Deeply fetching related objects is painless...bye, bye, JOINs.

select Movie {
  title,
  actors: {
    name
  }
}
filter .title = "The Matrix"

EdgeQL queries are also composable; you can use one EdgeQL query as an expression inside another. This property makes things like subqueries and nested mutations a breeze.

insert Movie {
  title := "The Matrix Resurrections",
  actors := (
    select Person
    filter .name in {
      'Keanu Reeves',
      'Carrie-Anne Moss',
      'Laurence Fishburne'
    }
  )
}

There's a lot more to EdgeQL: a comprehensive standard library, computed properties, polymorphic queries, with blocks, transactions, and much more. Read the EdgeQL docs for the full picture.


🦋 More than a mapper 🦋


While EdgeDB solves the same problems as ORM libraries, it's so much more. It's a full-fledged database with a powerful and elegant query language, a migrations system, a suite of client libraries in different languages, a command line tool, and—coming soon—a cloud hosting platform. The goal is to rethink every aspect of how developers model, migrate, manage, and query their database.

Here's a taste-test of EdgeDB's next-level developer experience: you can install our CLI, spin up an instance, and open an interactive EdgeQL shell with just three commands.

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.edgedb.com | sh
$ edgedb project init
$ edgedb
edgedb> select "Hello world!"

Windows users: use this Powershell command to install the CLI.

PS> iwr https://ps1.edgedb.com -useb | iex

Get started

To start learning about EdgeDB, check out the following resources:

  • The quickstart. If you're just starting out, the 10-minute quickstart guide is the fastest way to get up and running.
  • EdgeDB Cloud 🌤️. The best most effortless way to host your EdgeDB database in the cloud.
  • The interactive tutorial. For a structured deep-dive into the EdgeQL query language, try the web-based tutorial— no need to install anything.
  • The e-book. For the most comprehensive walkthrough of EdgeDB concepts, check out our illustrated e-book Easy EdgeDB. It's designed to walk a total beginner through EdgeDB in its entirety, from the basics through advanced concepts.
  • The docs. Jump straight into the docs for schema modeling or EdgeQL!

Contributing

PRs are always welcome! To get started, follow this guide to build EdgeDB from source on your local machine.

File an issue 👉
Start a Discussion 👉
Join the discord 👉


License

The code in this repository is developed and distributed under the Apache 2.0 license. See LICENSE for details.

edgedb-python's People

Contributors

0xsirsaif avatar 1st1 avatar adrienpensart avatar aeros avatar ambv avatar andreaspb avatar chrizzftd avatar colinhacks avatar dhghomon avatar dnwpark avatar elliotwaite avatar elprans avatar fantix avatar florimondmanca avatar fmoor avatar fogapod avatar jaclarke avatar mkniewallner avatar msullivan avatar mvaled avatar nsidnev avatar quinchs avatar raddevon avatar tailhook avatar tech-cobber avatar unmade avatar vpetrovykh 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  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  avatar  avatar

Watchers

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

edgedb-python's Issues

Feature request: AsyncIOPool.free_size property

Summary

free_size property should return the amount of unacquired/unestablished connections.
There could also be public min_size and max_size properties.

Reason

free_size property will be useful calculating pressure on pool.
While it could be counted from outside with custom wrapper or maybe with on_acquire / on_release callbacks, that would add some overhead. It could be implemented in pool itself without any overhead.

Test fail when running locally

                                                                         
========================================================================== FAILURES ==========================================================================
________________________________________________________ TestProto.test_proto_codec_error_recovery_01 ________________________________________________________
Traceback (most recent call last):                                                                                                                            
  File "/work/tests/test_proto.py", line 36, in test_proto_codec_error_recovery_01                                                                            
    self.con.fetchall("SELECT <local_date>'0001-01-01 BC';")                   
  File "/work/edgedb/blocking_con.py", line 42, in fetchall                                                                                                   
    query, args, kwargs)                                                       
  File "edgedb/protocol/blocking_proto.pyx", line 74, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous                             
    return self._iter_coroutine(self.execute_anonymous(*args, **kwargs))                                                                                      
  File "edgedb/protocol/blocking_proto.pyx", line 60, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine                                    
    coro.send(None)                                                                                                                                           
  File "edgedb/protocol/protocol.pyx", line 482, in execute_anonymous                                                                                         
    ret = await self._execute(in_dc, out_dc, args, kwargs)                                                                                                    
  File "edgedb/protocol/protocol.pyx", line 307, in _execute                                                                                                  
    raise exc                                                                                                                                                 edgedb.errors.InvalidValueError: invalid input syntax for type std::local_date: '0001-01-01 BC'                                                               
________________________________________________________ TestProto.test_proto_codec_error_recovery_02 ________________________________________________________
Traceback (most recent call last):                                                                                                                            
  File "/work/tests/test_proto.py", line 60, in test_proto_codec_error_recovery_02                                                                            
    """)                                                                                                                                                        File "/work/edgedb/blocking_con.py", line 42, in fetchall                                                                                                   
    query, args, kwargs)                                                                                                                                      
  File "edgedb/protocol/blocking_proto.pyx", line 74, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous                             
    return self._iter_coroutine(self.execute_anonymous(*args, **kwargs))       
  File "edgedb/protocol/blocking_proto.pyx", line 60, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine       
    coro.send(None)                                                            
  File "edgedb/protocol/protocol.pyx", line 482, in execute_anonymous                                                                                         
    ret = await self._execute(in_dc, out_dc, args, kwargs)                     
  File "edgedb/protocol/protocol.pyx", line 307, in _execute                                                                                                  
    raise exc                                                                                                                                                 
edgedb.errors.InvalidValueError: invalid input syntax for type std::local_date: '0001-01-01 BC'                                                               
===================================================================== 2 failed in 15.14s =======================

Note the error is expected:

            with self.assertRaises(edgedb.ClientError):
                # Python dattime.Date object can't represent this date, so
                # we know that the codec will fail.
                # The test will be rewritten once it's possible to override
                # default codecs.
                self.con.fetchall("SELECT <local_date>'0001-01-01 BC';")

Just different kind of error. Maybe, something with error hierarchy. Perhaps, it changed recently? (I'm using edgedb from master)

It looks like it's for testing error recovery, so replacing error with EdgeDBError might be good enough?

Feature request: a way to convert edgedb.Object to dict

Summary

Currently Object class does not look like dict and different tools do not understand how to use it.
My suggestion is to add Object.to_dict method or inherit from python dict to allow direct use.

Use case

Converting query result to pydantic model:

class User(pydantic.BaseModel):
    id: uuid.UUID
    nickname: str
    created_at: datetime.datetime
    bio: Optional[str] = None

async def get(db: edgedb.AsyncIOConnection, *, id: uuid.UUID) -> Optional[User]:
    result = await db.fetchall(
        """
        SELECT User {
            id,
            nickname,
            bio,
            created_at,
        }
        FILTER
            .id = <uuid>$id
            AND .email_vefified = true
        LIMIT 1
        """,
        id=id,
    )

    if not result:
        return None

    return User.parse_obj(result[0])  # unable to parse edgedb.Object

There are workarounds to this neither of which look good enough:

  • custom function for converting (not convenient, probably not efficient)
  • fetching results with fetchone_json and then using parse_raw pydantic method, but it's very library specific, forces the use of fetchone over fetchall and inefficient (double json conversion)

Why `execute()` doesn't support arguments?

My understanding that switching from fetchone to execute should be seamless if you don't need the result. But it's not the case:

  File "app.py", line 76, in login
    await request.app['edb'].execute("""
TypeError: execute() got an unexpected keyword argument 'cookie_id'

The error is confusing.

Technically, I understand that it's because there is such thing in a protocol. And that thing in protocol
may make sense for scripts or something, but it doesn't make sense from the point of view of the user of python bindings (other database bindings have no such issue as far as I remember).

So perhaps execute should accept arguments and silently switch to fetchone internally when at least one argument is specified?

Make connection.transaction() return an already-started transaction

connection.transaction() should be analogous to open() in that it should return an already-started transaction, instead of requiring an explicit call to start().

Two reasons:

  1. Pre-started transaction is a very weird thing.
  2. We obtain nice symmetry for context manager vs no context manager cases:
tx = connection.transaction()
# ...
tx.commit()

# and

with connection.transaction() as tx:
    ...

[Security] Potential Secret Leak

It has been noticed that while using edgedb/action-release/merge@master your gpg_key_id 5C468778062D87BF! is present in plaintext. Please ensure that secrets are encrypted or not passed as plain text in github workflows.

Cannot terminate from within transaction

import asyncio
import edgedb


async def main():
    client = edgedb.create_async_client("_localdev")
    async for tx in client.transaction():
        async with tx:
            print(await tx.query_single("SELECT 123"))
            client.terminate()
    await client.aclose()


asyncio.run(main())

This fails with:

123
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    asyncio.run(main())
  File "asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "test.py", line 10, in main
    client.terminate()
  File "edgedb/asyncio_pool.py", line 595, in terminate
    self._impl.terminate()
  File "edgedb/asyncio_pool.py", line 473, in terminate
    ch.terminate()
  File "edgedb/asyncio_pool.py", line 201, in terminate
    self._con.terminate()
  File "edgedb/asyncio_con.py", line 501, in terminate
    self._cleanup()
  File "edgedb/asyncio_pool.py", line 68, in _cleanup
    self._inner._holder._release_on_close()
  File "edgedb/asyncio_pool.py", line 204, in _release_on_close
    self._release()
  File "edgedb/asyncio_pool.py", line 217, in _release
    self._con = self._con._detach()
  File "edgedb/asyncio_pool.py", line 56, in _detach
    new_conn = self._shallow_clone()
  File "edgedb/asyncio_con.py", line 216, in _shallow_clone
    raise base_con.borrow_error(self._inner._borrowed_for)
  File "edgedb/base_con.py", line 45, in borrow_error
    raise errors.InterfaceError(BORROW_ERRORS[condition])
edgedb.errors.InterfaceError: Connection object is borrowed for a transaction. Use the methods on transaction object instead.

A more common use case is to wait on client.aclose() for some seconds, then CancellationError will trigger client.terminate() if some other coroutine is busy in a transaction.

Object's __hash__ and __eq__ are out of sync

Object.__hash__ takes into account all fetched pointers, essentially the hashing is similar to tuples.

Object.__eq__ and other compare operators compare by object's EdgeDB ID.

Possibly undefined exception class InternalClientError

asyncio_pool refers to errors.InternalClientError in two conditions, but there is no custom exception InternalClientError defined in errors namespace.

Should errors.InternalClientError be replaced with ClientError or one of its subclasses?
The name also rings a bell, because what does it mean for a client error to be "internal"?

Noticed while working on JS pool.

Type annotations

Hi!

First, thanks for all the great work on EdgeDB. The Python driver looks fab, and I especially like the built-in async support.


Problem description
From what I've seen, edgedb-python public-facing APIs do not provide type annotations. I discovered this by using edgedb.async_connect() and wondering why my editor wouldn't autocomplete the con object.

Proposed solution
Since edgedb-python is targeting Python 3.6+, I believe it should fully embrace type hints. Adding these would add a lot in terms of IDE support and developer productivity.

Implementation ideas
If not, I'd be very glad to give a helping hand adding these to the library. The PR might be quite big — should we consider splitting the work by tackling public-facing APIs only first? Let me know!

Datatypes Overview

Tuple and NamedTuple

edgedb.Tuple and edgedb.NamedTuple are conceptually similar to their Python equivalents. The exception is that their actual APIs and implementation is controlled by the edgedb library and thus we can introduce lazy decoding in the future.

edgedb.Tuple should be a duck-type of Python's built-in tuple type, with the exception of index() and count() methods not being implemented.

Both Tuple and NamedTuple are immutable, orderable, hashable, and pickeable.

class Tuple:

    def __init__(self, from_iter: Optional[Iterable[Any]]=None, 
                 *elements: Any):
        """Tuple can be created from an iterable or from positional args."""

    def __getitem__(self, index: int) -> Any:
        """Return an element by its index."""

    def __eq__(self, other: Union[Tuple, tuple]) -> bool:
        """Check if `self` is equal to `other`.

        Tuples returned from EdgeDB should be equal to
        Python tuples if they have the same elements.
        """

As in EdgeQL, named tuples are essentially tuples with an extra annotation. Therefore Tuple(1, 2) is equal to NamedTuple(a=1, b=2) and to NamedTuple(b=1, a=2). They are also equal to Python's (1, 2) tuple.

class NamedTuple(Tuple):

    def __init__(self, 
                 from_items: Optional[Union[Iterable[Tuple[str, Any]], Dict]]=None, 
                 **items: Any):
        """NamedTuples can be created from an iterable of (key, value)-like
        items or from **kwargs."""

    def __getattr__(self, name):
        """Return tuple's element by its name."""

    @staticmethod
    def fields(nt: NamedTuple) -> tuple[str]:
        """Return tuple's fields names (in the actual order)."""

    @staticmethod
    def as_dict(nt: NamedTuple) -> dict[str, Any]:
        """Return a dict build from tuple's field-value mapping."""

Objects and Links

edgedb.Object type is what EdgeDB objects are serialized into. It's an immutable, hashable and pickleable type.

Note, that Objects cannot be instantiated from Python (at least for the foreseeable future).

class Object:

    def __getattr__(self, name: str) -> Union[Any, Object, Set[Object], Set[Any]]:
        """Resolve object's attribute or link.

        Returns:

        * a scalar value, if `name` points to a property.
        * a set of scalars, if `name` is a property pointing to a set of scalars.
        * an Object, if `name` link has a '1' cardinality.
        * a Set of Objects, if `name` link has a '*' cardinality.
        """

    def __getitem__(self, name: str) -> Union[Link, LinkSet]:
        """Return a Link object by its name.

        Note that the method does not work for properties and will raise a
        `TypeError` if called with a property name passed to it.

        Returns:

        * a Link, if `name` link has a '1' cardinality.
        * a LinkSet, if `name` link has a '*' cardinality.  
        """

Object.__getitem__ can return a Link or a LinkSet. This API is used to access link properties.

class Link:

    @property
    def source(self) -> Object:
        """Return the source object for the link."""

    @property
    def target(self) -> Object:
        """Return link's target."""

    def __getattr__(self, name: str) -> Any:
        """Return the value of the `name` link property."""

class LinkSet:

    def __iter__(self) -> Iterator[Link]:
        """Allows to iterate over the Link objects of this set."""

    def __getitem__(self, target: Object) -> Link:
        """Return a Link object that points to the 'target' object."""

Sets and Arrays

edgedb.Set and edgedb.Array objects are similar to Python's list except that they are immutable. Inherently, they are also hashable, orderable, and pickeable.

Open Questions

  • Should we have Object.__iter__ method to make it simpler to cast Objects into Python dicts? I don't particularly like that option, since we now have Object.__getitem__ which returns Link|LinkSet, and Object.__getattr__ which returns concrete objects/values. __iter__ now looks like some introspection API; maybe we'd be better off having a designated top-level egdedb.object_to_dict() function or Object.as_dict() static method.

Doesn't work on python3.8

Error is:

  File "edgedb/protocol/protocol.pyx", line 1, in init edgedb.protocol.protocol
  File "edgedb/pgproto/./uuid.pyx", line 162, in init edgedb.pgproto.pgproto
TypeError: multiple bases have instance lay-out conflict

Edgedb-python version is 0.6.1 (latest on pypi at the moment)

EdgeQLSyntaxError: Unexpected '('

In EdgeDB, I built up a database schema in an .esdl file that has types with commonly shared properties like title and _title. Initially, I thought it would be nice to select either all of the parent type objects, like where (_title := 'CPU'), or instead zoom into just one of the extended child types, like (title := 'CPU_Iowait'). This turned out to be a problem when following the documentation at EdgeDB Documentation where the page suggests using the lexicon index on (__subject__.<prop>).

Expected Behavior

The edgedb-python lex checker should potentially accept a type definition like the following snippet, provided that I haven't missed something super obvious.

type Host {
    required property title -> str;
    multi link disks -> Disk;
    multi link children -> Host;
    annotation description := 'The Essilor-Luxottica hostname';
    index on (__subject__.title);
}

Current Behavior

When calling:

with open("schema.esdl", "r") as f:
    schema = f.read()
conn = edgedb.connect(...)
with conn.transaction():
    try:
        conn.execute(schema)
    except Exception as e:
        raise(e)

Python returns:

Traceback (most recent call last):
  File "setup.py", line 44, in <module>
    Database.build_schema()
  File "setup.py", line 42, in build_schema
    conn.execute(schema)
  File "setup.py", line 40, in build_schema
    self._protocol.sync_simple_query(query)
  File "edgedb/protocol/blocking_proto.pyx", line 76, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_simple_query
  File "edgedb/protocol/blocking_proto.pyx", line 59, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine
  File "edgedb/protocol/protocol.pyx", line 456, in simple_query
edgedb.errors.EdgeQLSyntaxError: Unexpected '('

Context (Environment)

I've encountered this error in OS environments Debian 10 and Ubuntu 18.04 using Python 3.7-dev.

Additionally, my schema.esdl file contains the following:

CREATE MIGRATION datatypes TO {
    type Unit {
        property title -> str;
        annotation description := 'The extensible unit type. If unit is `null`, then assign empty-set `{}`.';
    }
    type Rate {
        property title -> str;
        required property value -> float32;
        annotation description := 'The extensible rate type. If rate is `null`, then assign empty-set `{}`.';
    }
    type Resolution {
        required property milliseconds -> int32;
        annotation description := 'Resolution in milliseconds UTC';
    }
    type AggregationType {
        required property title -> str;
        annotation description := 'Any one of ["AVG", "SUM", "MIN", "MAX"]';
    }
    type Time {
        required property utc -> int64;
        annotation description := 'The approximate UTC time as a 64-bit integer';
    }
    type Percent extending Unit {
        required property value -> float32;
        annotation description := 'Ratio of resource unit to resource capacity';
    }
    type Bytes extending Unit {
        required property value -> int64;
        annotation description := 'Number of Bytes as a 64-bit integer';
    }
    type BytesPerSecond extending Rate {
        required property value -> float32;
        annotation description := 'Rate of Bytes per Second (B/s)';
    }
    type Count extending Unit {
        required property value -> int64;
        annotation description := 'The sum of units counted';
    }
    type CountPerSecond extending Rate {
        required property value -> float32;
        annotation description := 'The count of instances per second (count/s)';
    }
    type MilliSeconds extending Unit {
        required property value -> float32;
        annotation description := 'The number of milliseconds';
    }
    type Timeseries {
        annotation description := 'The abstract base type for a dynatrace Timeseries API call';
        required link aggregationType -> AggregationType;
        required link time -> Time;
        required link resolution -> Resolution;
    }
    type Host {
        required property title -> str;
        multi link disks -> Disk;
        multi link children -> Host;
        annotation description := 'The luxottica host group';
        index on (__subject__.title);
    }
    type CPU extending Timeseries {
        property _title -> str; # _title := 'CPU'
        required link value -> Percent;
        multi link hosts -> Host;
        index on (__subject__._title);
    }
    type Disk extending Timeseries {
        property _title -> str; # _title := 'Disk'
        required link host -> Host;
        required property path -> str;
        annotation description := 'The absolute path on a given host.';
        index on (__subject__._title);
    }
    type Memory extending Timeseries {
        property _title -> str; # _title := 'Memory'
        required link host -> Host;
        index on (__subject__._title);
    }
    type CPU_Idle extending CPU {
        property title -> str; # title := 'CPU_Idle'
        annotation description := 'Percent of Idle CPU usage';
        index on (__subject__.title);
    }
    type CPU_Iowait extending CPU {
        property title -> str; # title := 'CPU_Iowait'
        annotation description := 'Percent of Iowait-ing CPU usage';
        index on (__subject__.title);
    }
    type CPU_Other extending CPU {
        property title -> str; # title := 'CPU_Other'
        annotation description := 'Percent of other CPU usage';
        index on (__subject__.title);
    }
    type CPU_Steal extending CPU {
        property title -> str; # title := 'CPU_Steal'
        annotation description := 'Percent of stolen CPU usage'
        index on (__subject__.title);
    }
    type CPU_System extending CPU {
        property title -> str; # title := 'CPU_System'
        annotation description := 'Percent of system CPU usage';
        index on (__subject__.title);
    }
    type CPU_User extending CPU {
        property title -> str; # title := 'CPU_User'
        annotation description := 'Percent of user CPU usage';
        index on (__subject__.title); 
    }
    type Disk_Available extending Disk {
        annotation description := 'Disk space available in bytes (B) broken down by Host->Folder';
        required link value -> Bytes;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_BytesRead extending Disk {
        annotation description := 'Disk read bytes per second (B/s) broken down by Host->Folder';
        required link value -> BytesPerSecond;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_BytesWritten extending Disk {
        annotation description := 'Disk write bytes per second (B/s) broken down by Host->Folder';
        required link value -> BytesPerSecond;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_FreeSpacePercentage extending Disk {
        annotation description := 'Available Disk Percent (%) broken down by Host->Folder';
        required link value -> Percent;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_QueueLength extending Disk {
        annotation description := "Appx count of the disk's queue length for a given host's folder";
        required property value -> float32;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_ReadOperations extending Disk {
        annotation description := "Approximate disk read operations per second (count/s) for a given host's folder";
        required link value -> CountPerSecond;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_ReadTime extending Disk {
        annotation description := "Millisecs of time reading the disk for a given host's folder";
        required link value -> MilliSeconds;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_UsedSpace extending Disk {
        annotation description := 'Num bytes (B) used by the host for a particular disk (folder)';
        required link value -> Bytes;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_WriteOperations extending Disk {
        annotation description := 'Apx # write_ops/s (Count/sec) by a host at a specified folder.';
        required link value -> CountPerSecond;
        property title -> str;
        index on (__subject__.title);
    }
    type Disk_WriteTime extending Disk {
        annotation description := 'MilliSeconds (ms) of time writing to the disk by the host to a particular disk (folder).';
        required link value -> MilliSeconds;
        property title -> str;
        index on (__subject__.title);
    }
    type Memory_Available extending Memory {
        annotation description := 'Bytes of memory avilable on a host';
        required link value -> Bytes;
        property title -> str;
        index on (__subject__.title);
    }
    type Memory_AvailablePercentage extending Memory {
        annotation description := 'Percent of memory available on a host';
        required link value -> Percent;
        property title -> str;
        index on (__subject__.title);
    }
    type Memory_PageFaults extending Memory {
        annotation description := 'Apx # of page_faults/s (Count/sec) on a host';
        required property value -> float32;
        property title -> str;
        index on (__subject__.title);
    }
    type Memory_Used extending Memory {
        annotation description := 'Bytes of memory used on a host';
        required link value -> Bytes;
        property title -> str;
        index on (__subject__.title);
    }
};

COMMIT MIGRATION datatypes;

Miscellaneous

I found a potentially related issue on the main EdgeDB GitHub. If anyone knows the solution to this problem, there's reputation points up for grabs on Stack Overflow: My Issue

`py.typed` is not included in the package dist

I've noticed that package dist on PyPI is missing py.typed. Without that file Mypy is failing with:

error: Skipping analyzing 'edgedb': found module but no type hints or library stubs

This can be easily fixed by listing py.typed in MANIFEST.in

Passing in set datatype to query

When trying to run the below query:
r = con.query('with module inventory select Device {ip, hostname, site} filter .site in <set>$site', site={'site1', 'site3'})

I get the following error:
edgedb.errors.EdgeQLSyntaxError: Unexpected 'set'

I have also tried <Set>, <set<str>> but I get the same error.

Could anyone please help on the correct way to do this?

Thank you.

No documentation on using DNS with TLS

I started a new beta-3 instance and tried connecting to the DB from python. But I can't.

When I use the instance name it works fine

conn = edgedb.connect('edgedb_quickstart')

But when I tried using the DSN address its not as simple

conn = edgedb.connect('edgedb://edgedb:wL2JLb2G7By1KlbAd7uhZ49t@localhost:10701/edgedb')

ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1131)

Confusing KeyError on resulting row

On code like this:

x = edb.fetchone("SELECT ...")
x["id"]

I get error like this:

KeyError: 'id'

But I think it should be something like this:

TypeError: 'User' object is not subscriptable

Because basically I should write x.id instead (and it works).

Or is there any legitimate case when both attribute and item access work on the same row?

(lib version edgedb==0.7.0a2.dev0+e147670)

TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str'

(.venv) isidentical@search-tree-science-1:~/reiz.io$ ./scripts/regen_db.sh 
Stopping the server...
Re-starting the server...
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/isidentical/reiz.io/reiz/db/reset.py", line 49, in <module>
    main()
  File "/home/isidentical/reiz.io/reiz/db/reset.py", line 45, in main
    drop_and_load_db(**vars(options))
  File "/home/isidentical/reiz.io/reiz/db/reset.py", line 25, in drop_and_load_db
    with closing(edgedb.connect(dsn, database="edgedb")) as connection:
  File "/home/isidentical/reiz.io/.venv/lib/python3.8/site-packages/edgedb/blocking_con.py", line 244, in connect
    addrs, params, config = con_utils.parse_connect_arguments(
  File "/home/isidentical/reiz.io/.venv/lib/python3.8/site-packages/edgedb/con_utils.py", line 339, in parse_connect_arguments
    addrs, params = _parse_connect_dsn_and_args(
  File "/home/isidentical/reiz.io/.venv/lib/python3.8/site-packages/edgedb/con_utils.py", line 202, in _parse_connect_dsn_and_args
    path = (pathlib.Path.home() /
TypeError: unsupported operand type(s) for +: 'PosixPath' and 'str'

Possible fix:

+++ b/edgedb/con_utils.py
@@ -200,7 +200,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
                 f"nor valid instance name"
             )
         path = (pathlib.Path.home() /
-                '.edgedb' / 'credentials' / dsn + '.json')
+                '.edgedb' / 'credentials' / dsn).with_suffix('.json')
         try:
             creds = credentials.read_credentials(path)
         except Exception as e:

Response time issue

I am connecting to EdgeDB-SERVER in a GCP VM, using FASTAPI, code is given below
API code file and edgedb.toml are already in the same directory.

conn = edgedb.create_async_client()
app = FastAPI()

@app.get("/add_data")
async def adddata (data : dict):
    logging.info(f"-----------TIME ---------------  {datetime.now()}")
    st = datetime.now()
    var = await ( conn.query_json(INSERT  QUERY) 
    logging.info("Query exec time:  {}".format(datetime.now()-st))
    return eval(var)

@app.get("/get_single_data/{img_id}")
async def get_single_data(img_id:str):
    logging.info(f"-----------TIME st ---------------  {datetime.now()}")
    st = datetime.now()
    var = await ( conn.query_json(SELECT query) 
    logging.info("Query exec time:  {}".format(datetime.now()-st))
    return eval(var)

I am sending 20 requests using async + aiohttp.clientSession from different VM.
All requests are entered to api_function asynchronously but fetching from DB happens sequentially and the query exec time keeps on increasing.

while sending requests to /add_data, requests are going to FASTAPI concurrently

INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.887357
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.888552
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.889711
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.890296
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.890883
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.891310
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.891754
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.892140
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.892500
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.892883
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.893255
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.893613
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.894031
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.894564
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.895049
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.895480
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.895916
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.896332
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.896777
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.897791
INFO:root:-----------TIME ---------------  2022-01-25 07:47:48.898413
INFO:root:Query exec time: 0:00:00.011458
INFO:     10.56.3.98:57708 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.011933
INFO:     10.56.3.98:57736 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.013192
INFO:     10.56.3.98:57734 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.018139
INFO:     10.56.3.98:57712 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.020113
INFO:     10.56.3.98:57716 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.023454
INFO:     10.56.3.98:57718 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.017702
INFO:     10.56.3.98:57742 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.020796
INFO:     10.56.3.98:57714 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.022909
INFO:     10.56.3.98:57722 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.017524
INFO:     10.56.3.98:57744 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.018848
INFO:     10.56.3.98:57732 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.025497
INFO:     10.56.3.98:57724 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.022834
INFO:     10.56.3.98:57710 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.024485
INFO:     10.56.3.98:57720 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.020375
INFO:     10.56.3.98:57740 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.021983
INFO:     10.56.3.98:57738 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.025868
INFO:     10.56.3.98:57726 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.024466
INFO:     10.56.3.98:57728 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.025383
INFO:     10.56.3.98:57730 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.021126
INFO:     10.56.3.98:57746 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.022242

while sending requests to /get_single_data, requests are entering to API concurrently...

INFO:root:-----time st -------------2022-01-25 07:48:44.223958
INFO:root:-----time st -------------2022-01-25 07:48:44.225382
INFO:root:-----time st -------------2022-01-25 07:48:44.225925
INFO:root:-----time st -------------2022-01-25 07:48:44.226363
INFO:root:-----time st -------------2022-01-25 07:48:44.226710
INFO:root:-----time st -------------2022-01-25 07:48:44.227059
INFO:root:-----time st -------------2022-01-25 07:48:44.227408
INFO:root:-----time st -------------2022-01-25 07:48:44.227738
INFO:root:-----time st -------------2022-01-25 07:48:44.228052
INFO:root:-----time st -------------2022-01-25 07:48:44.228346
INFO:root:-----time st -------------2022-01-25 07:48:44.228631
INFO:root:-----time st -------------2022-01-25 07:48:44.228911
INFO:root:-----time st -------------2022-01-25 07:48:44.230125
INFO:root:-----time st -------------2022-01-25 07:48:44.230465
INFO:root:-----time st -------------2022-01-25 07:48:44.230788
INFO:root:-----time st -------------2022-01-25 07:48:44.231142
INFO:root:-----time st -------------2022-01-25 07:48:44.231471
INFO:root:-----time st -------------2022-01-25 07:48:44.231831
INFO:root:-----time st -------------2022-01-25 07:48:44.232150
INFO:root:-----time st -------------2022-01-25 07:48:44.232625
INFO:root:-----time st -------------2022-01-25 07:48:44.233109
INFO:root:Query exec time:  0:00:00.043368
INFO:     10.56.3.98:58302 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.082232
INFO:     10.56.3.98:58292 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.115055
INFO:     10.56.3.98:58298 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.148064
INFO:     10.56.3.98:58300 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.209334
INFO:     10.56.3.98:58306 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.238325
INFO:     10.56.3.98:58288 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.262985
INFO:     10.56.3.98:58296 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.292190
INFO:     10.56.3.98:58286 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.324947
INFO:     10.56.3.98:58284 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.347185
INFO:     10.56.3.98:58290 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.372092
INFO:     10.56.3.98:58294 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.400004
INFO:     10.56.3.98:58308 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.422483
INFO:     10.56.3.98:58318 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.450572
INFO:     10.56.3.98:58310 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.480537
INFO:     10.56.3.98:58320 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.506605
INFO:     10.56.3.98:58312 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.532549
INFO:     10.56.3.98:58322 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.564470
INFO:     10.56.3.98:58324 - "GET /get_single_data/rear_1632383205 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.594593
INFO:     10.56.3.98:58316 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.628099
INFO:     10.56.3.98:58304 - "GET /get_single_data/front_1632382854 HTTP/1.1" 200 OK
INFO:root:Query exec time:  0:00:00.658299
INFO:     10.56.3.98:58314 - "GET /get_single_data/hood_1632382967 HTTP/1.1" 200 OK

The main thing which I worried about is the Query exec time: 0:00:00.658299, it's increasing continuously and I am not able to understand when I send the requests, again and again, Query exec time is increasing. At the initial stage, the Query exec time was roughly 2-3 ms for a single request using postman.
in \add_data response >> Query_exec_time is not increasing sequentially

INFO:root:Query exec time: 0:00:00.025868
INFO:     10.56.3.98:57726 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.024466
INFO:     10.56.3.98:57728 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.025383
INFO:     10.56.3.98:57730 - "POST /add_data HTTP/1.1" 200 OK
INFO:root:Query exec time: 0:00:00.021126

But in the case of \get_single_data , its increasing sequentially and too large, which I am not able to understand
While sending requests, I was expecting that 20 requests will go concurrently, (which is happening) and Query_exec_time should be roughly 2-3 ms for each request. (Correct me here if I am wrong). Need help here

@fantix

Schema management

For comment:

  • Add an .edsl reader interface (blocking/ not async)
  • Add validation for .edsl files (blocking/ not async)
  • Add a direct migrate or apply_schema interface (blocking/ not async)
  • Add a direct export interface, (also blocking) perhaps something like:

from pathlib import Path
from typing import Union
export(db: str, path: Path, tables: Union[set, list, tuple]) # what other options?

My expectation is that these are not likely to and should not be run in an async context, but maybe I'm just not aware of the use case. I would be happy to work on this piece of the project.

edgedb orm

Hey, guys! Not really much people(and most of newbies) are usually writing raw db requests using execute.
So I'm sure that will be a good feature if you'll create your personal ORM specially for edgedb instead of SQLAlchemy, Tortoise ORM or something like that.

Have you got it in ur plans?

Forking process with edgedb connection is dangerous

In particular, if connection is used in both forks, it looks like this:

    Error on request:
    Traceback (most recent call last):
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/serving.py", line 323, in run_wsgi
        execute(self.server.app)
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/serving.py", line 312, in execute
        application_iter = app(environ, start_response)
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/wrappers/base_request.py", line 238, in application
        resp = f(*args[:-2] + (request,))
      File "/work/serve_py_sync/main.py", line 48, in application
        return ROUTES[endpoint](request, **values)
      File "/work/serve_py_sync/main.py", line 39, in increment
        """, name=name)
      File "/usr/local/lib/python3.6/dist-packages/edgedb/blocking_con.py", line 117, in query_one
        io_format=protocol.IoFormat.BINARY,
      File "edgedb/protocol/blocking_proto.pyx", line 91, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous

      File "edgedb/protocol/blocking_proto.pyx", line 77, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine

      File "edgedb/protocol/protocol.pyx", line 588, in execute_anonymous

      File "edgedb/protocol/protocol.pyx", line 269, in _parse

      File "edgedb/protocol/protocol.pyx", line 1057, in edgedb.protocol.protocol.SansIOProtocol.fallthrough

    edgedb.errors.ProtocolError: unexpected message type 'T'Error on request:
    Traceback (most recent call last):
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/serving.py", line 323, in run_wsgi
        execute(self.server.app)
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/serving.py", line 312, in execute
        application_iter = app(environ, start_response)
      File "/usr/local/lib/python3.6/dist-packages/werkzeug/wrappers/base_request.py", line 238, in application
        resp = f(*args[:-2] + (request,))
      File "/work/serve_py_sync/main.py", line 48, in application
        return ROUTES[endpoint](request, **values)
      File "/work/serve_py_sync/main.py", line 39, in increment
        """, name=name)
      File "/usr/local/lib/python3.6/dist-packages/edgedb/blocking_con.py", line 117, in query_one
        io_format=protocol.IoFormat.BINARY,
      File "edgedb/protocol/blocking_proto.pyx", line 91, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous

      File "edgedb/protocol/blocking_proto.pyx", line 77, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine

      File "edgedb/protocol/protocol.pyx", line 588, in execute_anonymous

      File "edgedb/protocol/protocol.pyx", line 310, in _parse

      File "edgedb/protocol/protocol.pyx", line 1057, in edgedb.protocol.protocol.SansIOProtocol.fallthrough

    edgedb.errors.ProtocolError: unexpected message type '1'

So the issue is similar to #130.

We may use os.register_at_fork to fix this in python3.7.

Seems like v0.13.0a3 broke async log listeners

In edgedb/edgedb:

$ edb test -k test_server_proto_log_message_01

FAIL: test_server_proto_log_message_01 (test_server_proto.TestServerProto)


Traceback (most recent call last):
  File "/home/elvis/dev/edgedb/edgedb/edb/testbase/server.py", line 123, in wrapper
    self.loop.run_until_complete(
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/elvis/dev/edgedb/edgedb/tests/test_server_proto.py", line 801, in test_server_proto_log_message_01
    raise AssertionError('a notice message was not delivered')
AssertionError: a notice message was not delivered

Lots of `DeprecationWarning` on `AsyncIOPool` methods

  • edgedb: 1-beta3
  • edgedb-python: 0.17.2

Whenever I call methods on AsyncIOPool I get a lot a of warnings like in the pytest output:

/Users/unmade/src/unmade/shelf-back/.venv/lib/python3.9/site-packages/edgedb/asyncio_pool.py:619: DeprecationWarning: The "acquire()" method is deprecated and is scheduled to be removed. Use the query methods on Pool instead.
    async with self.acquire() as con:
/Users/unmade/src/unmade/shelf-back/.venv/lib/python3.9/site-packages/edgedb/asyncio_pool.py:775: DeprecationWarning: The "release()" method is deprecated and is scheduled to be removed. Use the query methods on Pool instead.
    await self.pool.release(con)

The reason for these warning is that AsyncIOPool itself uses deprecated methods like acquire and release.

Although I can these warnings only in pytest output and I can suppress them I still want to ask is this expected behaviour or not?

Support the trio event loop

I'd very much would like to use this library but I use trio.
Using AnyIO instead of asyncio directly would enable using both asyncio (with or without uvloop) and trio.

Unable to connect edgedb instance running in GCP VM

Instance running in VM
$ edgedb instance list
┌───────┬────────────┬───────┬──────────────────┬─────────┐
│ Kind │ Name │ Port │ Version │ Status │
├───────┼────────────┼───────┼──────────────────┼─────────┤
│ local │ quickstart │ 10700 │ 1.0-rc.3+147eb62 │ running │
└───────┴────────────┴───────┴──────────────────┴─────────┘
I want to connect to instance from outside of vm using
conn = edgedb.connect(host= "vm external ip", port=10700)
but still not able to connect. getting error
ClientConnectionFailedTemporarilyError: [Errno 111] Connection refused After 114 attempts in 30.1 sec Is the server running on host 'VM external IP' and accepting TCP/IP connections on port 10700?
firewall rules and network tags checked already.

@elprans @1st1 @tailhook @fantix

Broken `.with_transaction_options` and `.with_retry_options` on `AsyncIOPool`

I'm on the latest edgedb-python (0.14.0) with EdgeDB 1-beta2 running in Docker

I've tried to call with_retry_options method on AsyncIOPool instance, but it fails with:

        ...
        result = self._shallow_clone()
>      result._options = self._options.with_retry_options(options)
E      AttributeError: 'NoneType' object has no attribute '_options'

I've also noticed that AsyncIOPool doesn't implement _shallow_clone required by _OptionsMixin.

Also, is there is any specific reason why _OptionsMixin doesn't inherit from abc.ABC but still uses abstractmethod decorator?

Better error for mismatched query parameters and arguments

The code like:

await c.fetchall("""
    INSERT Foo {
        smth := <str>$smth,
        other := <datetime>$other,
        ...
        # a dozen lines skipped
        ...
        quux := <int16>$quux
    }
""", **some_dict)

Fails with:

Traceback (most recent call last):
  File "/Users/dima.tisnek/edge/ingest.py", line 47, in <module>
    asyncio.run(main())
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/Users/dima.tisnek/edge/ingest.py", line 14, in main
    await c.fetchall("""
  File "/Users/dima.tisnek/Library/Caches/pypoetry/virtualenvs/edge-tfNuXqpZ-py3.9/lib/python3.9/site-packages/edgedb/asyncio_con.py", line 99, in fetchall
    return await self._protocol.execute_anonymous(
  File "edgedb/protocol/protocol.pyx", line 534, in execute_anonymous
  File "edgedb/protocol/protocol.pyx", line 279, in _execute
  File "edgedb/protocol/protocol.pyx", line 987, in edgedb.protocol.protocol.SansIOProtocol.encode_args
  File "edgedb/protocol/./codecs/./namedtuple.pyx", line 74, in edgedb.protocol.protocol.NamedTupleCodec.encode_kwargs
RuntimeError: expected 18 keyword arguments, got 17

Clearly the keys in somedict don't match the query parameters...

It would be awesome to know which keys :)

getting ClientConnectionClosedError randomly

I am connecting to EdgeDB-SERVER in a GCP VM, using FASTAPI, code is given below
API code file and edgedb.toml are already in the same directory.

conn = edgedb.connect() 
@app.post("/add_data")   
def add_data(data : Item):
    logging.info(f"Connection_is_closed() : {conn.is_closed()}")
    if conn.is_closed():
        conn._reconnect()
    if conn:
        return conn.query_json("some query here..")

Whenever I send a request got my response
but randomly get ClientConnectionClosedError.

just before and after the error

  • request sent, got an error
INFO:root:Connection_is_closed() : False
INFO:     IP:55358 - "POST /add_batch_data_uc HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):  LONG ERROR BELOW
  • when I resent/retry the request, got my response, no error
INFO:root:Connection_is_closed() : True
INFO:root:Query exec time: 0:00:00.013752
INFO:     IP:55366 - "POST /add_batch_data_uc HTTP/1.1" 200 OK

INFO:root:Connection_is_closed() : False
INFO:root:Query exec time: 0:00:00.014205
INFO:     IP:55366 - "POST /add_batch_data_uc HTTP/1.1" 200 OK
  • same problem happens randomly when I try to execute conn.json(query) in random_test.ipynb. It works most of the time but sometimes I got ClientConnectionClosedError
import asyncio
import datetime
import edgedb 
conn= edgedb.connect()
conn.query_json( a query inside )    # generates error randomly / rarely
  • ERROR
INFO:root:Connection_is_closed() : False
INFO:root:query_execution_time : 0:00:00.015267
INFO:     IP_address:54516 - "POST /add_batch_data_uc HTTP/1.1" 200 OK

INFO:root:Connection_is_closed() : False
INFO:     IP_address:24256 - "POST /add_batch_data_uc HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
ERROR:uvicorn.error:Exception in ASGI application
Traceback (most recent call last):
  File "/home/user/.local/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 376, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/user/.local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "/home/user/.local/lib/python3.8/site-packages/fastapi/applications.py", line 208, in __call__
    await super().__call__(scope, receive, send)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/applications.py", line 112, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc
  File "/home/user/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc
  File "/home/user/.local/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/routing.py", line 656, in __call__
    await route.handle(scope, receive, send)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/routing.py", line 259, in handle
    await self.app(scope, receive, send)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/routing.py", line 61, in app
    response = await func(request)
  File "/home/user/.local/lib/python3.8/site-packages/fastapi/routing.py", line 226, in app
    raw_response = await run_endpoint_function(
  File "/home/user/.local/lib/python3.8/site-packages/fastapi/routing.py", line 161, in run_endpoint_function
    return await run_in_threadpool(dependant.call, **values)
  File "/home/user/.local/lib/python3.8/site-packages/starlette/concurrency.py", line 39, in run_in_threadpool
    return await anyio.to_thread.run_sync(func, *args)
  File "/home/user/.local/lib/python3.8/site-packages/anyio/to_thread.py", line 28, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
  File "/home/user/.local/lib/python3.8/site-packages/anyio/_backends/_asyncio.py", line 818, in run_sync_in_worker_thread
    return await future
  File "/home/user/.local/lib/python3.8/site-packages/anyio/_backends/_asyncio.py", line 754, in run
    result = context.run(func, *args)
  File "main.py", line 106, in add_batch_data
    var = (conn.query_json('''INSERT uniform_comp {
  File "/home/user/.local/lib/python3.8/site-packages/edgedb/blocking_con.py", line 379, in query_json
    return self._execute(
  File "/home/user/.local/lib/python3.8/site-packages/edgedb/blocking_con.py", line 342, in _execute
    raise e
  File "/home/user/.local/lib/python3.8/site-packages/edgedb/blocking_con.py", line 314, in _execute
    return self._get_protocol().sync_execute_anonymous(
  File "edgedb/protocol/blocking_proto.pyx", line 105, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous
  File "edgedb/protocol/blocking_proto.pyx", line 91, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine
  File "edgedb/protocol/protocol.pyx", line 677, in execute_anonymous
  File "edgedb/protocol/protocol.pyx", line 474, in _optimistic_execute
  File "edgedb/protocol/blocking_proto.pyx", line 62, in wait_for_message
edgedb.errors.ClientConnectionClosedError

INFO:root:Connection_is_closed() : True
INFO:root:query_execution_time : 0:00:00.017633
INFO:     IP_address:54530 - "POST /add_batch_data_uc HTTP/1.1" 200 OK

INFO:root:Connection_is_closed() : False
INFO:root:query_execution_time : 0:00:00.014480
INFO:     IP_address:54530 - "POST /add_batch_data_uc HTTP/1.1" 200 OK

@elprans @fantix @1st1 @tailhook

Not handling `None` arguments correctly

>>> c.fetchone('select <int64>$0 + 1', None)
Traceback (most recent call last):
  File "edgedb/protocol/protocol.pyx", line 409, in edgedb.protocol.protocol.SansIOProtocol._optimistic_execute
  File "edgedb/protocol/protocol.pyx", line 1089, in edgedb.protocol.protocol.SansIOProtocol.parse_data_messages
  File "edgedb/protocol/./codecs/./scalar.pyx", line 31, in edgedb.protocol.protocol.ScalarCodec.decode
  File "edgedb/pgproto/./codecs/int.pyx", line 118, in edgedb.pgproto.pgproto.int8_decode
  File "edgedb/pgproto/./frb.pxd", line 28, in edgedb.pgproto.pgproto.frb_read
  File "edgedb/pgproto/./frb.pyx", line 10, in edgedb.pgproto.pgproto.frb_check
AssertionError: insufficient data in buffer: requested 8 remaining 0

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/yury/dev/venvs/edgedb/lib/python3.8/site-packages/edgedb/blocking_con.py", line 107, in fetchone
    return self._protocol.sync_execute_anonymous(
  File "edgedb/protocol/blocking_proto.pyx", line 91, in edgedb.protocol.blocking_proto.BlockingIOProtocol.sync_execute_anonymous
  File "edgedb/protocol/blocking_proto.pyx", line 77, in edgedb.protocol.blocking_proto.BlockingIOProtocol._iter_coroutine
  File "edgedb/protocol/protocol.pyx", line 555, in execute_anonymous
  File "edgedb/protocol/protocol.pyx", line 445, in _optimistic_execute
edgedb.errors.ClientError: unable to decode data to Python objects

ValueError: 'edgedb/pgproto/pgproto.pyx' doesn't match any files

Reproduction steps:

$ pyenv install 3.9-dev
$ git clone https://github.com/edgedb/edgedb-python
$ pyenv local 3.9-dev; pyenv shell
$ # Update pip and install wheel ...
$ cd edgedb-python
$ pip install -e . 

results in ...

Obtaining file:///home/edgemorph/edgemorph/edm/bootstrap/edgedb-python
    ERROR: Command errored out with exit status 1:
    (... snip ...)
      File "/home/edgemorph/edgemorph/edm/bootstrap/edgedb-python/setup.py", line 252, in finalize_options
        self.distribution.ext_modules[:] = cythonize(
      File "/home/edgemorph/edgemorph/edm/bootstrap/edgedb-python/.eggs/Cython-0.29.14-py3.9-linux-x86_64.egg/Cython/Build/Dependencies.py", line 959, in cytho
nize
        module_list, module_metadata = create_extension_list(
      File "/home/edgemorph/edgemorph/edm/bootstrap/edgedb-python/.eggs/Cython-0.29.14-py3.9-linux-x86_64.egg/Cython/Build/Dependencies.py", line 810, in creat
e_extension_list
        for file in nonempty(sorted(extended_iglob(filepattern)), "'%s' doesn't match any files" % filepattern):
      File "/home/edgemorph/edgemorph/edm/bootstrap/edgedb-python/.eggs/Cython-0.29.14-py3.9-linux-x86_64.egg/Cython/Build/Dependencies.py", line 109, in nonem
pty
        raise ValueError(error_msg)
    ValueError: 'edgedb/pgproto/pgproto.pyx' doesn't match any files
    ----------------------------------------
WARNING: Discarding file:///home/edgemorph/edgemorph/edm/bootstrap/edgedb-python. Command errored out with exit status 1: python setup.py egg_info Check the lo
gs for full command output.
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

and as you'd probably expect:

$ python setup.py egg_info
running egg_info
creating edgedb.egg-info
writing edgedb.egg-info/PKG-INFO
writing dependency_links to edgedb.egg-info/dependency_links.txt
writing requirements to edgedb.egg-info/requires.txt
writing top-level names to edgedb.egg-info/top_level.txt
writing manifest file 'edgedb.egg-info/SOURCES.txt'
Traceback (most recent call last):
  File "/home/edgemorph/edgemorph/edm/bootstrap/edgedb-python/setup.py", line 278, in <module>
    (... snip ...)
ValueError: 'edgedb/pgproto/pgproto.pyx' doesn't match any files

Am I missing something?

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.