Giter Club home page Giter Club logo

goblin-legacy's People

Contributors

davebshow avatar dusktreader avatar h-plus-time avatar jsenecal avatar leifurhauks avatar poelzi 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

goblin-legacy's Issues

Moving forward with Goblin - Utilities for working with Titan:db schema and indices

Hello everyone! I would like to discuss some plans for moving forward with goblin. The are some implementation details that still need some work, like error handling, logging, etc., but the library is basically up and running. The next big hurdle will be figuring out how to implement some tools for working with Titan's modelling API. I noticed that Cody implemented some model syncing functionality in the original mogwai library, but I haven't looked too closely at it, and I don't know if it ever was actually integrated into the main library.

My initial thought is to provide a series of functions that can perform schema definitions supported by Titan:db [1]. Basically, we would be able to:

  1. Create new elements (vertex, edge, property), and define their data types as well as cardinality.
  2. Change element names (Titan does not allow changing data type etc.).
  3. Define static vertices, TTL and undirected edges [2].

Regarding the implementation of this functionality, I want to do something like already suggested by the original mogwai design. The process would be something like this:

>>> generate_specfile(path_to_model_definition="models.py", ...)

The generate_specfile function inspects the file containing the goblin model definition and creates a simple JSON style data structure that contains the vertices, edges, and associated properties, which is dumped to a file current_spec.json.

"[{
    "verticies": [{"label": "person", ..., "properties": []}]
    "edges": ...
}]"

We then check to see if there is already a spec file associated with the app. If there is, we do a series of simple comparisons to check if there are:

  1. Missing Vertices/Edges/Properties
  2. New Vertices/Edge/Properties
>>> sync_spec(new_specfile, old_specfile)

If there are missing properties, we add this to a list of unused properties that can be reused. Missing vertices/edges are cataloged as unused elements. If there are "new properties" (could be an old property with a name change), we look through the unused properties. If there is an unused property with the proper data type and cardinality, we can recycle it by changing its name using Titan's API. If there are new vertices/edges, they will be created. Optionally, we could allow the user to pass element renaming mappings to allow name changes on vertices and edges, or other functionality to help the process along.

Finally, we output a new spec file that reflects the current state of the Schema, which includes the name changes made during the sync, as well as all of the unused elements.

Of course, if this is the first sync, we simply create the schema based on the specfile,

While this system isn't perfect, it could be pretty handy, especially for adding new models or simple property name changes.

Regarding indices [3], I assume we would like to allow the user to define their own indices on the models, which will be created on model sync. Elements created before index definition can be easily reindexed.

Any ideas @jsenecal or @leifurhauks?

  1. http://s3.thinkaurelius.com/docs/titan/1.0.0/schema.html
  2. http://s3.thinkaurelius.com/docs/titan/1.0.0/advanced-schema.html
  3. http://s3.thinkaurelius.com/docs/titan/1.0.0/indexes.html

tried running basic example

Used conda to create the environment (python3.5)
ran pip install git+https://github.com/ZEROFAIL/goblin.git@dev#egg=goblin

tried running the basic example code (all imports work)

ERROR:tornado.application:Exception in callback <function execute_query..on_connect at 0x104f01158> for <tornado.concurrent.Future object at 0x104a814e0>
Traceback (most recent call last):

ile "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 317, in _set_done
cb(self)
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/goblin/connection.py", line 80, in on_connect
request_id=request_id)
TypeError: send() got an unexpected keyword argument 'request_id'

Later on i've did some reading, edited titan-1.0.0-hadoop1/conf/gremlin-server/gremlin-server.yaml
changed channelizer to :
channelizer: org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer

restarted titan server and response:
Traceback (most recent call last):
File "basic.py", line 30, in
followers = loop.run_sync(go)
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/ioloop.py", line 453, in run_sync
return future_cell[0].result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/gen.py", line 1014, in run
yielded = self.gen.throw(*exc_info)
File "basic.py", line 18, in go
goblin = yield User.create(name="Goblin")
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/gen.py", line 1008, in run
value = future.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/goblin/models/vertex.py", line 368, in on_save
stream = f.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/goblin/gremlin/base.py", line 291, in on_call
stream = f.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/goblin/connection.py", line 73, in on_connect
conn = f.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/gremlinclient/pool.py", line 124, in cb
conn = f.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/gremlinclient/tornado_client/client.py", line 116, in get_conn
conn = f.result()
File "/Users/blurb/miniconda3/envs/titan/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
raise_exc_info(self._exc_info)
File "", line 3, in raise_exc_info
tornado.httpclient.HTTPError: HTTP 400: Bad Request

Default labels for derived Vertex classes are not working

So, I am working on deriving a class from the Goblin Vertex class. In the source code, it seems to indicate that the .label property will be automatically set from the class name, but I haven’t found this to be the case.

Here is my source:

from goblin import properties
from goblin import connection
from goblin.models import Vertex

import asyncio
from tornado.platform.asyncio import AsyncIOMainLoop


class TestVertex(Vertex):
    prop1 = properties.String()


@asyncio.coroutine
def drop():
    stream = yield from TestVertex.all()
    vertices = yield from stream.read()
    for vertex in vertices:
        yield from vertex.delete()


@asyncio.coroutine
def create():
    yield from TestVertex.create(prop1='A')
    yield from TestVertex.create(prop1='B')


@asyncio.coroutine
def report():
    stream = yield from TestVertex.all()
    vertices = yield from stream.read()
    for vertex in vertices:
        print(vertex.label)
        print(vertex)


if __name__ == '__main__':
    connection.setup('ws://localhost:8182', future_class=asyncio.Future)
    AsyncIOMainLoop().install()
    loop = asyncio.get_event_loop()
    try:
        print("dropping")
        loop.run_until_complete(drop())
        print("creating")
        loop.run_until_complete(create())
        print("reporting")
        loop.run_until_complete(report())
        print("ending")
    except Exception as err:
        print("Caught an errror: {}".format(err))
        raise err
    finally:
        connection.tear_down()
        loop.close()

And the output:

$ python test.py
dropping
creating
reporting
None
TestVertex(label=test_vertex, id=20560, values={'prop1': 'B'})
None
TestVertex(label=test_vertex, id=12384, values={'prop1': 'A'})
ending

I would expect that the line above that says 'None' should say 'TestVertex'

Second code example from README is out of date

I noticed that the second code example seems to be out of date. In particular, the 'future' keyword argument to connection.setup should be 'future_class'

I don't know if there are other issues or not; this was the only one I discovered

Implementing time based versioning

Hello all,

I wanted to discuss the implementation of time based versioning and get your feedback. After looking through Ian Robinson's Neo4j Time-Based Versioned Graphs blog [1], I think that his approach can be adapted to fit goblin.

I'm not going to summarize the contents of the article, but here are the different options he proposes:

  1. Basic model - Identity Nodes, Structural Relationships, State Nodes and Relationships.
  2. Linked List - using a linked list structure to order state nodes (allows for sequential state traversal)
  3. Current State Relationship - A special rel that points from the ident node to the current state node.
  4. Initial State Overload - Overloading ident node with initial state.
  5. Current State Overload - Overloading ident node with current state

These options can be combined in a variety of different ways, but not all combinations are possible. All use the Basic Model as a base. Linked Lists can optionally be combined with any possible combination. Possible combos:

  1. Current State Relationship
  2. Current State Relationship w/Initial State Overload
  3. Initial State Overload
  4. Current State Overload

Therefore, to implement these combinations, I propose that we provide: a) a new set of kwargs representing these options that will be passed to Vertex methods create, delete, save; and b) a subclass of Vertex, VersionedVertex that is versioned by default.

The kwargs and defaults would be as follows:

versioned=False
linked=False
overload_current=False
overload_initial=False
current_state=False

In the case that restricted combinations occur, kwargs will be treated as follows:

versioned=False overrides all other args
overload_current=True overrides current_state=True

if overload_current and overload_initial:
   raise Exception

What else do we need? Feedback?

  1. http://iansrobinson.com/2014/05/13/time-based-versioned-graphs/

Goblin property review/comparison with Titan

It's time to update Goblin's property management to improve integration with Titan.

To review there are currently a wide variety of properties that map between Python and Titan as follows:

Goblin Python 2/3 Titan
String String String
Short Int Short
Integer Long/Integer Integer
Positive Integer Long/Integer Integer
Long Long/Integer Long
Positive Long Long/Integer Long
Double Float Double
Float Float Float
UUID String String
Decimal Decimal Decimal/Precision (3/6 dig)
Dictionary Dictionary N/A
List List N/A
URL String String
DateTimeNaive Datetime Date?
DateTime Datetime Date?
Boolean Boolean Boolean
Slug String String
IPV4/6/4w/6 String String

Most of these properties map simply and only needed to be tested against a graph db with appropriate schema definition. However, there are a few things to consider:

You may note that dictionary and list no longer have corresponding data types in Titan, instead, you can define a property data type as well as a cardinality--single, list, or set. We'll need to provide an API that allows the user to set the cardinality.

Furthermore, the current Titan native data types make a distinction between 3/6 digit decimals (Decimal and Precision), and we should probably reflect this either by providing kwarg options or separate property classes.

The slug property doesn't do any slugification, something that may be of interest...

Also, the geoshape type will need to be implemented.

I haven't double checked yet how Titan handles the Date data type. Mogwai sent unix timestamps. Any input on the API, needed properties, anything...?

@jsenecal @leifurhauks?

_simple_traversal in Edge class does not return future

On line 433, the _simple_traversal method does not return anything. It definitely seems like it should return the future_results variable that has nothing assigned to it

    future_results = connection.execute_query(
        'g.e(id).%s()' % operation, {'id': self.id},
        handler=edge_traversal_handler, **kwargs)
    return

Dropping support for Python 2.7?

We are seeking input from the community about the need for goblin to support python 2.7. Are you using goblin with python2? If you are using, or planning to use, goblin, please comment below and let us know what version of python you are using.

We are seriously considering dropping support for python 2.7 for several reasons, including (but not limited to) the following:

  • In order to support both python 2.7 (with tornado) and python 3.3+ (with the choice of tornado or asyncio), goblin's code base is currently written in callback style. This makes for code that is significantly more difficult to read, debug, and update compared to code that uses the tools available for writing asynchronous code in python 3.3+. Removing support for 2.7 would allow us to lift the goblin codebase out of callback hell and move more quickly on bugfixes and feature development.
  • Tinkerpop and Titan are fairly new technologies. Python 2.x had already been the legacy version of python for some time when titan saw its first release. Thus we think it unlikely that there are a lot of legacy python2 projects written for titan. Furthermore, converting an application written for a traditional database to leverage Titan necessitates extensive redesign; this is arguably a lot more involved than porting a project from python 2 to python 3, which for most projects requires a lot of small changes but doesn't really need to involve fundamentally redesigning an app. This is especially true now that the vast majority of major python packages have been ported to python 3.
  • The future of asynchronous programming in python (not to mention the future of the python language as a whole) is all about python 3

No VERSION file when I "pip install git+https://github.com/ZEROFAIL/goblin.git@dev"

I am a former Mogwai user and am excited to try out goblin. When I pip-ed into my python environment, discovered the VERSION file was not present. I added it manually and am moving forward, but thought you should know.

Upon further examination, it appears all of the files considered Unix executables - edge.groovy, vertex.groovy, LICENSE, Makefile, MANIFEST.in ... are all missing.

DateTime type doesn't take a datetime

Here is a simple code sample:


from goblin import properties
from goblin.models import Vertex, Edge, V
from goblin.connection import setup, close_global_pool

from datetime import datetime

class myVx(Vertex):

    someString   = properties.String()
    someDateTime = properties.DateTime()

setup("localhost")

myDT = datetime.now()

myNewVx = myVx.create(someString = "Hello World",
                      someDateTime = myDT)


I was surprised when I got this exception:

Traceback (most recent call last):
  File "vxtest.py", line 17, in <module>
    someDateTime = myDT)
  File "/some/path/.virtualenvs/goblin/lib/python3.5/site-packages/goblin/models/element.py", line 258, in create
    return cls(*args, **kwargs).save(**query_kwargs)
  File "/some/path/.virtualenvs/goblin/lib/python3.5/site-packages/goblin/models/element.py", line 69, in __init__
    value = prop.to_python(value)
  File "/some/path.virtualenvs/goblin/lib/python3.5/site-packages/goblin/properties/properties.py", line 227, in to_python
    float(value)).replace(tzinfo=utc)
TypeError: float() argument must be a string or a number, not 'datetime.datetime'

When I look at the relevant block of code in properties.py:

    def to_python(self, value):
        try:
            if isinstance(value, datetime.datetime):
                if value.tzinfo == utc:
                    return self.validator(value)  # .astimezone(tz=utc)
                else:
                    return self.validator(value).astimezone(tz=utc)
        except Exception as e:  # pragma: no cover
            # this shouldn't happen unless the validator has changed
            pass
        return datetime.datetime.utcfromtimestamp(
            float(value)).replace(tzinfo=utc)

If I expose the exception that is actually being thrown:

  File "/some/path/.virtualenvs/goblin/lib/python3.5/site-packages/goblin/properties/validators.py", line 170, in __call__
    self.message.format(value), code=self.code)
goblin.exceptions.ValidationError: Not a valid UTC DateTime: 2016-03-21 17:08:39.149907

So, looking at that block of code, I end up in this try/except chain:

            # print_("Got value with timezone: {} - {}".format(value, value.tzinfo))
            try:
                value = value.astimezone(tz=utc)
            except ValueError:  # last ditch effort
                try:
                    value = value.replace(tz=utc)
                except (AttributeError, TypeError):
                    raise ValidationError(
                        self.message.format(value), code=self.code)
            except AttributeError:  # pragma: no cover
                # This should never happen, unless it isn't a datetime object
                raise ValidationError(self.message % (value, ), code=self.code)

value.astimezone(tz=utc) fails with the exception:
ValueError: astimezone() cannot be applied to a naive datetime

So that drops to value.replace(tz=utc), which fails with the exception:
TypeError: 'tz' is an invalid keyword argument for this function

And that is probably because the keyword is supposed to be tzinfo .

All of that analysis makes me wonder if the default behavior, when passing in a timezone-naive datetime, should be to assume UTC, or should it assume "Local Timezone" before stashing it in the database? Perhaps a warning could be issued?

FWIW, to default to the local timezone I, usually do this quick and dirty approach:

from tzlocal import get_localzone
from datetime import datetime

myDT = datetime.now()
myDT = myDT.replace(tzinfo = get_localzone())

... and when I add that to my example above, the new vertex is successfully created (because it bypasses the faulty validator code).

Goblin Docs

Before I add the new functionality for schema and indices management, I think we should provide some simple documentation that covers goblin's core functionality, both to help our current users get going and attract new users/contributors. The first step in getting going will be updating the gremlinclient docs, as I would like to provide good cross referencing to help goblin users understand how to customize their goblin app. This will be relatively quick, but the goblin docs will be a bit more work. Before I put the time in, I want to make sure we are all on the same page.

I plan on using Sphinx to generate the documentation and host the docs on readthedocs, pretty standard, but there are other options for hosting as well...I usually use the alabaster theme, but I am open to suggestions here...

Relevant API documentation will be auto-generated by Sphinx from the docstrings, but I think it is important to include hand written docs with examples as well. I was thinking a basic structure like this:

  • Home/Getting Started
  • User Guides
    • The Basics - Defining Models and Creating Vertices and Relationships
    • Vertex and Relationship Objects API
    • The Relationship API - Adding Relationships to Vertex Models
    • Vertex Centric Queries
  • Choosing a Websocket Client - Tornado, Aiohttp, Pulsar, Requests
  • Schema and Index Management (coming soon)
  • API autodocs
    ...

Comments or suggestions @jsenecal @leifurhauks?

Vertex.get : unexpected exception for nonexistent ID

Calling Vertex.get with a nonexistent ID doesn't raise a DoesNotExist as expected.

When I run this script:
https://gist.github.com/leifurhauks/34c8e4d28025cd579af6

I get the following output:

leifurpc% python silly_script.py
INFO:__main__:set up goblin connection
INFO:__main__:Created person: Person(label=person, id=40972480, values={'name': 'Margaretta Heathcote DDS', 'url': 'http://www.jenkins.info/', 'email': '[email protected]'})
INFO:__main__:ID: 40972480
INFO:__main__:Got a copy of person by id: Person(label=person, id=40972480, values={'name': 'Margaretta Heathcote DDS', 'url': 'http://www.jenkins.info/', 'email': '[email protected]'})
ERROR:asyncio:Exception in callback Stream._read.<locals>.parser.<locals>.<lambda>(<Task finishe...> result=None>) at /home/lasgeirsson/.virtualenvs/noss-graphdb/lib/python3.5/site-packages/gremlinclient/connection.py:330
handle: <Handle Stream._read.<locals>.parser.<locals>.<lambda>(<Task finishe...> result=None>) at /home/lasgeirsson/.virtualenvs/noss-graphdb/lib/python3.5/site-packages/gremlinclient/connection.py:330>
Traceback (most recent call last):
  File "/usr/lib/python3.5/asyncio/events.py", line 125, in _run
    self._callback(*self._args)
  File "/home/lasgeirsson/.virtualenvs/noss-graphdb/lib/python3.5/site-packages/gremlinclient/connection.py", line 330, in <lambda>
    lambda f: future.set_exception(e))
NameError: free variable 'e' referenced before assignment in enclosing scope

Goblin constructs property keys with hypercorrective zeal

Using the following model definition:

class Foo(models.Vertex):
    foo = properties.String(db_field='bar')

I have a 'foo' vertex in the database already with 'bar' = 'testing123'.

The following retrieves it and correctly prints 'testing123':

async def foo_property():
    foo = await Foo.get('4312')
    print(foo.bar)

If I now change foo.bar and save it:

async def set_foo_property(new_value):
    foo = await Foo.get('4312')
    foo.bar = new_value
    await foo.save()

The foo_property coroutine now prints 'different value' as expected. But now the 'bar' property appears to be mapped to a new property, 'foo_bar' in titan:

gremlin> g.V(4312).properties()
==>vp[bar->testing123]
==>vp[foo_bar->different value]
gremlin> 

all() method is raising a gremin error

I just started trying out goblin as my python binding to titan and with it the tinkerpop3 stack. I tried out a few simple operations and was able to get it working fairly easily. I was just about to start writing (re-writing from py2neo in fact) my models to use goblin, but ran into this error. It seems to be coming from gremlin, and I slimmed down my code to the essentials to test it out.

Here is the code:

 from goblin import properties                                                                       
 from goblin import connection                                                                       
 from goblin.models import Vertex                                                                    

 from tornado import gen                                                                             
 from tornado.ioloop import IOLoop                                                                   


 class TestVertex(Vertex):                                                                           
     prop1 = properties.String()                                                                     


 @gen.coroutine                                                                                      
 def go():                                                                                           
     yield TestVertex.create(prop1='A')                                                              
     yield TestVertex.create(prop1='B')                                                              
     stream = yield TestVertex.all()                                                                 
     vertices = yield stream.read()                                                                  
     for vertex in vertices:                                                                         
         print(vertex)                                                                               


 if __name__ == '__main__':                                                                          
     connection.setup('ws://localhost:8182')                                                         
     loop = IOLoop.current()                                                                         
     try:                                                                                            
         print("start")                                                                              
         loop.run_sync(go)                                                                           
         print("end")                                                                                
     except Exception as err:                                                                        
         print("Caught an errror: {}".format(err))                                                   
         raise err                                                                                   
     finally:                                                                                        
         connection.tear_down()                                                                      
         loop.close()   

And here is the error:

$ python test.py
start
Caught an errror: 597 No such property: V for class: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
Traceback (most recent call last):
  File "test.py", line 32, in <module>
    raise err
  File "test.py", line 28, in <module>
    loop.run_sync(go)
  File "/home/tbeck/.virtualenvs/goblin/lib/python3.5/site-packages/tornado/ioloop.py", line 453, in run_sync
    return future_cell[0].result()
  File "/home/tbeck/.virtualenvs/goblin/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
  File "/home/tbeck/.virtualenvs/goblin/lib/python3.5/site-packages/tornado/gen.py", line 1014, in run
    yielded = self.gen.throw(*exc_info)
  File "test.py", line 18, in go
    vertices = yield stream.read()
  File "/home/tbeck/.virtualenvs/goblin/lib/python3.5/site-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/home/tbeck/.virtualenvs/goblin/lib/python3.5/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
RuntimeError: 597 No such property: V for class: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource

I need some of these basic ORM type methods to be working well before I can use goblin. Is this a problem with my implementation or with goblin's interface to gremlin?

Support multi properties

I'm creating this issue to track what still needs to be done for Goblin to have full support of multi-properties. In Titan, this means properties with cardinality LIST or SET.

Currently, updating a multi-property from Goblin appends the new value. Deleting values from a multi-property doesn't seem to be possible yet.

all() method for edges results in None if no edges are found

The all() method results in a value of None from the stream future if no results are found.

Here is an example piece of code:

from goblin import properties
from goblin import connection
from goblin.models import Vertex, Edge

import inflection
import asyncio
from tornado.platform.asyncio import AsyncIOMainLoop


@asyncio.coroutine
def drop():
    stream = yield from Vertex.all()
    vertices = yield from stream.read()
    for vertex in vertices:
        yield from vertex.delete()
if __name__ == '__main__':
    connection.setup('ws://localhost:8182', future_class=asyncio.Future)
    AsyncIOMainLoop().install()
    loop = asyncio.get_event_loop()
    try:
        print("dropping")
        loop.run_until_complete(drop())
        loop.run_until_complete(drop())
        print("ending")
    except Exception as err:
        print("Caught an errror: {}".format(err))
        raise err
    finally:
        connection.tear_down()
        loop.close()

Executing this code will result in the follow exception:

TypeError: 'NoneType' object is not iterable

The result from the all method's stream should be an empty list so that an iteration over results where no matches are found does not raise an exception

all() method fails when more than 64 vertices of one type exist

I've found that the all() method raises an exception dependably when there are more than 64 vertices of one type in the graph db. I stripped my code down to the bare minimum and created an executable python script to demonstrate the issue

Here is the source code

#!/usr/bin/env python

import asyncio
from tornado.platform.asyncio import AsyncIOMainLoop

from goblin import properties, connection
from goblin.models import Vertex

class MyVertex(Vertex):
    prop1 = properties.Integer()

connection.setup('ws://localhost:8182', future_class=asyncio.Future)
AsyncIOMainLoop().install()

@asyncio.coroutine
def clear():
    result = yield from connection.execute_query('g.V().drop().iterate()')
    stream = yield from result.read()
    return stream

@asyncio.coroutine
def create(i):
    result = yield from MyVertex.create(prop1=i)
    return result

@asyncio.coroutine
def find():
    result = yield from MyVertex.all()
    stream = yield from result.read()
    return stream

loop = asyncio.get_event_loop()
def smash(n):
    print("Clearing old vertices")
    loop.run_until_complete(clear())
    print("Smashing {} vertices".format(n))
    for i in range(n):
        loop.run_until_complete(create(i))
    loop.run_until_complete(find())

n = 1
while True:
    smash(n)
    n += 1

And here is the output:

$ bin/smash
Clearing old vertices
Smashing 1 vertices
Clearing old vertices
Smashing 2 vertices
Clearing old vertices
Smashing 3 vertices
Clearing old vertices
Smashing 4 vertices
Clearing old vertices
Smashing 5 vertices
Clearing old vertices
Smashing 6 vertices
Clearing old vertices
Smashing 7 vertices
Clearing old vertices
Smashing 8 vertices
Clearing old vertices
Smashing 9 vertices
Clearing old vertices
Smashing 10 vertices
Clearing old vertices
Smashing 11 vertices
Clearing old vertices
Smashing 12 vertices
Clearing old vertices
Smashing 13 vertices
Clearing old vertices
Smashing 14 vertices
Clearing old vertices
Smashing 15 vertices
Clearing old vertices
Smashing 16 vertices
Clearing old vertices
Smashing 17 vertices
Clearing old vertices
Smashing 18 vertices
Clearing old vertices
Smashing 19 vertices
Clearing old vertices
Smashing 20 vertices
Clearing old vertices
Smashing 21 vertices
Clearing old vertices
Smashing 22 vertices
Clearing old vertices
Smashing 23 vertices
Clearing old vertices
Smashing 24 vertices
Clearing old vertices
Smashing 25 vertices
Clearing old vertices
Smashing 26 vertices
Clearing old vertices
Smashing 27 vertices
Clearing old vertices
Smashing 28 vertices
Clearing old vertices
Smashing 29 vertices
Clearing old vertices
Smashing 30 vertices
Clearing old vertices
Smashing 31 vertices
Clearing old vertices
Smashing 32 vertices
Clearing old vertices
Smashing 33 vertices
Clearing old vertices
Smashing 34 vertices
Clearing old vertices
Smashing 35 vertices
Clearing old vertices
Smashing 36 vertices
Clearing old vertices
Smashing 37 vertices
Clearing old vertices
Smashing 38 vertices
Clearing old vertices
Smashing 39 vertices
Clearing old vertices
Smashing 40 vertices
Clearing old vertices
Smashing 41 vertices
Clearing old vertices
Smashing 42 vertices
Clearing old vertices
Smashing 43 vertices
Clearing old vertices
Smashing 44 vertices
Clearing old vertices
Smashing 45 vertices
Clearing old vertices
Smashing 46 vertices
Clearing old vertices
Smashing 47 vertices
Clearing old vertices
Smashing 48 vertices
Clearing old vertices
Smashing 49 vertices
Clearing old vertices
Smashing 50 vertices
Clearing old vertices
Smashing 51 vertices
Clearing old vertices
Smashing 52 vertices
Clearing old vertices
Smashing 53 vertices
Clearing old vertices
Smashing 54 vertices
Clearing old vertices
Smashing 55 vertices
Clearing old vertices
Smashing 56 vertices
Clearing old vertices
Smashing 57 vertices
Clearing old vertices
Smashing 58 vertices
Clearing old vertices
Smashing 59 vertices
Clearing old vertices
Smashing 60 vertices
Clearing old vertices
Smashing 61 vertices
Clearing old vertices
Smashing 62 vertices
Clearing old vertices
Smashing 63 vertices
Clearing old vertices
Smashing 64 vertices
Clearing old vertices
Smashing 65 vertices
Traceback (most recent call last):
  File "bin/smash", line 43, in <module>
    smash(n)
  File "bin/smash", line 39, in smash
    loop.run_until_complete(find())
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 337, in run_until_complete
    return future.result()
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/tasks.py", line 241, in _step
    result = coro.throw(exc)
  File "bin/smash", line 29, in find
    stream = yield from result.read()
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py", line 358, in __iter__
    yield self  # This tells Task to wait for completion.
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/tasks.py", line 290, in _wakeup
    future.result()
  File "/usr/local/Cellar/python3/3.5.1/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/futures.py", line 274, in result
    raise self._exception
RuntimeError: 599 Error during serialization: Could not execute operation due to backend exception (through reference chain: java.util.ArrayList[0]

Regardless of how many
This is a serious issue because it is preventing me from using goblin at the moment until this has been resolved.

WTForms Integration

Hi guys
Do you plan to add WTForms support?
It would probably take only small modifications as WTForms already has a populate_obj() function.
I included a link to screenshots I made to explain how WTForms already works with the ODM MongoEngine, and how it could work with Goblin.
I think Goblin could be one of the first OGM to be interfaced directly to forms,
I don't think I'm enough skilled to modify Goblin codebase myself thought.

I included screenshots with the most complicated of scenarios (object that contains objects that contains objects)

In this example it is a City containing two Zoos containing Animals

The screenshots are here:

https://imgur.com/a/kFNto

note:

the mongoengine code shown must use mongoengine==0.8.7 and pymongo==2.8 to work

test_populate_obj.txt
test_goblin.txt

Create a vertex without a label

Is there a way to create a vertex sans label?

I've tried overriding Vertex's get_label in order to return None or '' but those both threw exceptions. I'm using goblin with an existing database which does not have any vertex labels (and has a strict schema which does not permit label creation on the fly).

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.