From e1187635ddad9f533f7b39f1334de01c435f7509 Mon Sep 17 00:00:00 2001 From: Eli Bixby Date: Thu, 18 Dec 2014 10:38:31 -0800 Subject: [PATCH 1/4] Added demo for usage input on gcloud revisions --- gcloud/demo.py | 296 ++++++++++++++++++++++++++++++------------------- 1 file changed, 181 insertions(+), 115 deletions(-) diff --git a/gcloud/demo.py b/gcloud/demo.py index 3ef2192ca15c..2e3c1beb072e 100644 --- a/gcloud/demo.py +++ b/gcloud/demo.py @@ -1,115 +1,181 @@ -# pragma NO COVER -from code import interact -import itertools -import os.path -import sys -import time -from six.moves import input - - -class DemoRunner(object): - """An interactive runner of demo scripts.""" - - KEYPRESS_DELAY = 0.05 - GLOBALS, LOCALS = globals(), locals() - CODE, COMMENT = 'code', 'comment' - - def __init__(self, fp): - self.lines = [line.rstrip() for line in fp.readlines()] - - @classmethod - def from_module(cls, module): - path = os.path.join(os.path.dirname(module.__file__), - 'demo', 'demo.py') - - return cls(open(path, 'r')) - - def run(self): - line_groups = itertools.groupby(self.lines, self.get_line_type) - - for group_type, lines in line_groups: - if group_type == self.COMMENT: - self.write(lines) - - elif group_type == self.CODE: - self.code(lines) - - interact('(Hit CTRL-D to exit...)', local=self.LOCALS) - - def wait(self): - input() - - @classmethod - def get_line_type(cls, line): - if line.startswith('#'): - return cls.COMMENT - else: - return cls.CODE - - def get_indent_level(self, line): - if not line.strip(): - return None - return len(line) - len(line.lstrip()) - - def _print(self, text='', newline=True): - sys.stdout.write(text) - if newline: - sys.stdout.write('\n') - - def write(self, lines): - self._print() - self._print('\n'.join(lines), False) - self.wait() - - def code(self, lines): - code_lines = [] - - for line in lines: - indent = self.get_indent_level(line) - - # If we've completed a block, - # run whatever code was built up in code_lines. - if indent == 0: - self._execute_lines(code_lines) - code_lines = [] - - # Print the prefix for the line depending on the indentation level. - if indent == 0: - self._print('>>> ', False) - elif indent > 0: - self._print('\n... ', False) - elif indent is None: - continue - - # Break the line into the code section and the comment section. - if '#' in line: - code, comment = line.split('#', 2) - else: - code, comment = line, None - - # 'Type' out the comment section. - for char in code.rstrip(): - time.sleep(self.KEYPRESS_DELAY) - sys.stdout.write(char) - sys.stdout.flush() - - # Print the comment section (not typed out). - if comment: - sys.stdout.write(' # %s' % comment.strip()) - - # Add the current line to the list of lines to be run - # in this block. - code_lines.append(line) - - # If we had any code built up that wasn't part of a completed block - # (ie, the lines ended with an indented line), - # run that code. - if code_lines: - self._execute_lines(code_lines) - - def _execute_lines(self, lines): - if lines: - self.wait() - - # Yes, this is crazy unsafe... but it's demo code. - exec('\n'.join(lines), self.GLOBALS, self.LOCALS) + + +from connection import Connection +from dataset import Dataset + + +conn = Connection(credentials) +d = Dataset('dataset-id-here', conn, namespace='demo') +#defaults: namespace=None, force=False, read_option="DEFAULT" +""" +a dataset object has 4 mutators and 3 accessors for the datastore +all mutations are immediately and synchronously applied +to the datastore, note however that since the dataset<-->connection +relationship is possibly many-to-one nothing prevents a user +from creating multiple datasets with the same id. +In this case the user is responsible for handling synchronicity +""" + +#the first three mutators upsert, insert, and update all have the same usage +d.upsert( + ('ent','root', 'ent', 'treebeard'), + dict(hates_sarumon=True, age=9001), #defaults {} + unindexed=['talking_speed']) #defaults [] + +#upsert executes regardless of the state of the entity +#update will fail on entities which are not in the datastore +#update and upsert completely overwrite entities +#insert will fail on entities which are already in the datastore + +#these three mutations can also insert using a partial id to automatically allocate and id +nameless_orc_key = d.upsert(('orc','root','orc'), dict(is_alive=True)) +#the allocated key is returned in it's entirety +#>>> print nameless_orc_key +#('orc','root','orc',int) + +d.delete('ent','root','ent','treebeard') +#delete simply uses the key path to delete entities + +nameless_orc_props = d.get(*nameless_orc_key) +#get uses the same arguments, but returns the property dict +print nameless_orc_props +#{'is_alive':True} + +#get_all() and query() will be covered later, but first... +#what if you want to insert a large number of entities, +#without slamming the datastore with calls + +""" +calling d.batch_mutator() returns a batch mutator with the same +initialization settings as the calling dataset +BatchMutators have the same mutators with the same signature +as Datasets, but do not have accessor methods + +Instead of immediately applying mutations, BatchMutators +accumulate them within a context, and apply the mutation +upon exiting a context. As such, auto-allocated keys will +not be returned upon insertion of partial keys. +Instead, a user who wants to retrieve theys keys calls +result() outside the context. +""" + +b = d.batch_mutator() + +try: + b.insert(('orc','root','orc')) +except: + print "mutations cannot be made outside of a context" + + +with b: + b.insert(('orc','root','orc', 'azog'), dict(is_alive=True)) + + azog = d.get('orc','root','orc', 'azog') + #dataset methods can still be called within a context + #however, changes made by the BulkMutator are not visible + #until the context exits + print azog + #None + + #on the other hand, BulkMutators can effect data mutated by the + #dataset during the context + d.insert(('hobbit','root','hobbit','mary'), dict(underrated=True)) + + b.delete('hobbit','root','hobbit','mary') + + for i in range(0,100): + b.insert(('orc','root','orc'), dict(is_alive=True)) + + +nameless_orc_army = b.result() + +mary = d.get('hobbit','root','hobbit','mary') +print mary +#None + +#also note that result() does not include keys with user- +#set names or ids +print ('orc','root','orc','azog') in nameless_orc_army +#False + + +""" +Transactions are objects that behave very similar to BatchMutators +however, they also have the accessor methods from Dataset +(and behave like transactions...) +""" + +d.insert(('ent','root','ent','treebeard'), dict(age=9001)) + +t = d.transaction() + +treebeard_props = d.get('ent','root','ent','treebeard') +treebeard_props['age'] += 1 +d.update(('ent','root','ent','treebeard'), treebeard_props) + +# t takes a snapshot of d, so changes made to d after t is +# initialized will not be visible inside the context + + + +with t: + + d.upsert(('hobbit','root','hobbit','frodo'), dict(underrated=False)) + #additionally, changes made while in the context will not be visible + #to transactional reads like t.get() or t.query() + + frodo = t.get('hobbit','root','hobbit','frodo') + print frodo + #None + + #if you need to get automatically allocate a key to mutate later in + #a transaction, you can call t.allocate_id(*key) + + important_orc_key = t.allocate_id('orc','root','orc') + t.upsert(important_orc_key, dict(carrying="explosives!", is_alive=True)) + + + treebeard_props = t.get('ent','root','ent','treebeard') + #t.get() gets 'younger' treebeard + treebeard_props['age'] += 1 + d.update(('ent','root','ent','treebeard'), treebeard_props) + +treebeard_props = d.get('ent','root','ent', 'treebeard') + +print treebeard_props['age'] +#9002 + + +""" +Using GQL is more consistent with the built-in-style of the library +Query syntax uses ordered arguments as the number-args, +and keyword arguments as the named-args, as well as a special arg +called "cursor" which is used for fetching pages +""" + +query_string = 'SELECT __key__ FROM @kind OFFSET @cursor WHERE HAS ANCESTOR @1 AND @2=@3' + +alive_orcs, more = d.query( + query_string, + 'KEY(\'orc\', \'root\')', + 'is_alive', + True, + kind='orc' + cursor=None) + +while(more): + more_alive_orcs, more = d.query( + query_string, + 'KEY(\'orc\', \'root\')', + 'is_alive', + True, + kind='orc' + cursor=more) + + alive_orcs.extend(more_alive_orcs) + +print ('orc', 'root', 'orc', 'azog') in alive_orcs +#True + +print important_orc_key in alive_orcs +#True From 033a9cdacc4d94a81adc44a9a1de8aa44bc5c594 Mon Sep 17 00:00:00 2001 From: Eli Bixby Date: Thu, 18 Dec 2014 10:52:24 -0800 Subject: [PATCH 2/4] Added usage demo for new gclout to correct place --- gcloud/datastore/demo/demo.py | 223 +++++++++++++++++------- gcloud/demo.py | 310 ++++++++++++++-------------------- 2 files changed, 290 insertions(+), 243 deletions(-) diff --git a/gcloud/datastore/demo/demo.py b/gcloud/datastore/demo/demo.py index 41c22922dfb0..2e3c1beb072e 100644 --- a/gcloud/datastore/demo/demo.py +++ b/gcloud/datastore/demo/demo.py @@ -1,82 +1,181 @@ -# pragma NO COVER -# Welcome to the gCloud Datastore Demo! (hit enter) -# We're going to walk through some of the basics... -# Don't worry though. You don't need to do anything, just keep hitting enter... -# Let's start by importing the demo module and getting a dataset: -from gcloud.datastore import demo -dataset = demo.get_dataset() +from connection import Connection +from dataset import Dataset -# Let's create a new entity of type "Thing" and name it 'Toy': -toy = dataset.entity('Thing') -toy.update({'name': 'Toy'}) -# Now let's save it to our datastore: -toy.save() +conn = Connection(credentials) +d = Dataset('dataset-id-here', conn, namespace='demo') +#defaults: namespace=None, force=False, read_option="DEFAULT" +""" +a dataset object has 4 mutators and 3 accessors for the datastore +all mutations are immediately and synchronously applied +to the datastore, note however that since the dataset<-->connection +relationship is possibly many-to-one nothing prevents a user +from creating multiple datasets with the same id. +In this case the user is responsible for handling synchronicity +""" -# If we look it up by its key, we should find it... -print(dataset.get_entities([toy.key()])) +#the first three mutators upsert, insert, and update all have the same usage +d.upsert( + ('ent','root', 'ent', 'treebeard'), + dict(hates_sarumon=True, age=9001), #defaults {} + unindexed=['talking_speed']) #defaults [] -# And we should be able to delete it... -toy.delete() +#upsert executes regardless of the state of the entity +#update will fail on entities which are not in the datastore +#update and upsert completely overwrite entities +#insert will fail on entities which are already in the datastore -# Since we deleted it, if we do another lookup it shouldn't be there again: -print(dataset.get_entities([toy.key()])) +#these three mutations can also insert using a partial id to automatically allocate and id +nameless_orc_key = d.upsert(('orc','root','orc'), dict(is_alive=True)) +#the allocated key is returned in it's entirety +#>>> print nameless_orc_key +#('orc','root','orc',int) -# Now let's try a more advanced query. -# We'll start by look at all Thing entities: -query = dataset.query().kind('Thing') +d.delete('ent','root','ent','treebeard') +#delete simply uses the key path to delete entities -# Let's look at the first two. -print(query.limit(2).fetch()) +nameless_orc_props = d.get(*nameless_orc_key) +#get uses the same arguments, but returns the property dict +print nameless_orc_props +#{'is_alive':True} -# Now let's check for Thing entities named 'Computer' -print(query.filter('name =', 'Computer').fetch()) +#get_all() and query() will be covered later, but first... +#what if you want to insert a large number of entities, +#without slamming the datastore with calls -# If you want to filter by multiple attributes, -# you can string .filter() calls together. -print(query.filter('name =', 'Computer').filter('age =', 10).fetch()) +""" +calling d.batch_mutator() returns a batch mutator with the same +initialization settings as the calling dataset +BatchMutators have the same mutators with the same signature +as Datasets, but do not have accessor methods -# You can also work inside a transaction. -# (Check the official docs for explanations of what's happening here.) -with dataset.transaction(): - print('Creating and savng an entity...') - thing = dataset.entity('Thing') - thing.key(thing.key().name('foo')) - thing['age'] = 10 - thing.save() +Instead of immediately applying mutations, BatchMutators +accumulate them within a context, and apply the mutation +upon exiting a context. As such, auto-allocated keys will +not be returned upon insertion of partial keys. +Instead, a user who wants to retrieve theys keys calls +result() outside the context. +""" - print('Creating and saving another entity...') - thing2 = dataset.entity('Thing') - thing2.key(thing2.key().name('bar')) - thing2['age'] = 15 - thing2.save() +b = d.batch_mutator() - print('Committing the transaction...') +try: + b.insert(('orc','root','orc')) +except: + print "mutations cannot be made outside of a context" -# Now that the transaction is commited, let's delete the entities. -print(thing.delete(), thing2.delete()) -# To rollback a transaction, just call .rollback() -with dataset.transaction() as t: - thing = dataset.entity('Thing') - thing.key(thing.key().name('another')) - thing.save() - t.rollback() +with b: + b.insert(('orc','root','orc', 'azog'), dict(is_alive=True)) -# Let's check if the entity was actually created: -created = dataset.get_entities([thing.key()]) -print('yes' if created else 'no') + azog = d.get('orc','root','orc', 'azog') + #dataset methods can still be called within a context + #however, changes made by the BulkMutator are not visible + #until the context exits + print azog + #None -# Remember, a key won't be complete until the transaction is commited. -# That is, while inside the transaction block, thing.key() will be incomplete. -with dataset.transaction(): - thing = dataset.entity('Thing') - thing.save() - print(thing.key()) # This will be partial + #on the other hand, BulkMutators can effect data mutated by the + #dataset during the context + d.insert(('hobbit','root','hobbit','mary'), dict(underrated=True)) -print(thing.key()) # This will be complete + b.delete('hobbit','root','hobbit','mary') -# Now let's delete the entity. -thing.delete() + for i in range(0,100): + b.insert(('orc','root','orc'), dict(is_alive=True)) + + +nameless_orc_army = b.result() + +mary = d.get('hobbit','root','hobbit','mary') +print mary +#None + +#also note that result() does not include keys with user- +#set names or ids +print ('orc','root','orc','azog') in nameless_orc_army +#False + + +""" +Transactions are objects that behave very similar to BatchMutators +however, they also have the accessor methods from Dataset +(and behave like transactions...) +""" + +d.insert(('ent','root','ent','treebeard'), dict(age=9001)) + +t = d.transaction() + +treebeard_props = d.get('ent','root','ent','treebeard') +treebeard_props['age'] += 1 +d.update(('ent','root','ent','treebeard'), treebeard_props) + +# t takes a snapshot of d, so changes made to d after t is +# initialized will not be visible inside the context + + + +with t: + + d.upsert(('hobbit','root','hobbit','frodo'), dict(underrated=False)) + #additionally, changes made while in the context will not be visible + #to transactional reads like t.get() or t.query() + + frodo = t.get('hobbit','root','hobbit','frodo') + print frodo + #None + + #if you need to get automatically allocate a key to mutate later in + #a transaction, you can call t.allocate_id(*key) + + important_orc_key = t.allocate_id('orc','root','orc') + t.upsert(important_orc_key, dict(carrying="explosives!", is_alive=True)) + + + treebeard_props = t.get('ent','root','ent','treebeard') + #t.get() gets 'younger' treebeard + treebeard_props['age'] += 1 + d.update(('ent','root','ent','treebeard'), treebeard_props) + +treebeard_props = d.get('ent','root','ent', 'treebeard') + +print treebeard_props['age'] +#9002 + + +""" +Using GQL is more consistent with the built-in-style of the library +Query syntax uses ordered arguments as the number-args, +and keyword arguments as the named-args, as well as a special arg +called "cursor" which is used for fetching pages +""" + +query_string = 'SELECT __key__ FROM @kind OFFSET @cursor WHERE HAS ANCESTOR @1 AND @2=@3' + +alive_orcs, more = d.query( + query_string, + 'KEY(\'orc\', \'root\')', + 'is_alive', + True, + kind='orc' + cursor=None) + +while(more): + more_alive_orcs, more = d.query( + query_string, + 'KEY(\'orc\', \'root\')', + 'is_alive', + True, + kind='orc' + cursor=more) + + alive_orcs.extend(more_alive_orcs) + +print ('orc', 'root', 'orc', 'azog') in alive_orcs +#True + +print important_orc_key in alive_orcs +#True diff --git a/gcloud/demo.py b/gcloud/demo.py index 2e3c1beb072e..b49fb37a538e 100644 --- a/gcloud/demo.py +++ b/gcloud/demo.py @@ -1,181 +1,129 @@ - - -from connection import Connection -from dataset import Dataset - - -conn = Connection(credentials) -d = Dataset('dataset-id-here', conn, namespace='demo') -#defaults: namespace=None, force=False, read_option="DEFAULT" -""" -a dataset object has 4 mutators and 3 accessors for the datastore -all mutations are immediately and synchronously applied -to the datastore, note however that since the dataset<-->connection -relationship is possibly many-to-one nothing prevents a user -from creating multiple datasets with the same id. -In this case the user is responsible for handling synchronicity -""" - -#the first three mutators upsert, insert, and update all have the same usage -d.upsert( - ('ent','root', 'ent', 'treebeard'), - dict(hates_sarumon=True, age=9001), #defaults {} - unindexed=['talking_speed']) #defaults [] - -#upsert executes regardless of the state of the entity -#update will fail on entities which are not in the datastore -#update and upsert completely overwrite entities -#insert will fail on entities which are already in the datastore - -#these three mutations can also insert using a partial id to automatically allocate and id -nameless_orc_key = d.upsert(('orc','root','orc'), dict(is_alive=True)) -#the allocated key is returned in it's entirety -#>>> print nameless_orc_key -#('orc','root','orc',int) - -d.delete('ent','root','ent','treebeard') -#delete simply uses the key path to delete entities - -nameless_orc_props = d.get(*nameless_orc_key) -#get uses the same arguments, but returns the property dict -print nameless_orc_props -#{'is_alive':True} - -#get_all() and query() will be covered later, but first... -#what if you want to insert a large number of entities, -#without slamming the datastore with calls - -""" -calling d.batch_mutator() returns a batch mutator with the same -initialization settings as the calling dataset -BatchMutators have the same mutators with the same signature -as Datasets, but do not have accessor methods - -Instead of immediately applying mutations, BatchMutators -accumulate them within a context, and apply the mutation -upon exiting a context. As such, auto-allocated keys will -not be returned upon insertion of partial keys. -Instead, a user who wants to retrieve theys keys calls -result() outside the context. -""" - -b = d.batch_mutator() - -try: - b.insert(('orc','root','orc')) -except: - print "mutations cannot be made outside of a context" - - -with b: - b.insert(('orc','root','orc', 'azog'), dict(is_alive=True)) - - azog = d.get('orc','root','orc', 'azog') - #dataset methods can still be called within a context - #however, changes made by the BulkMutator are not visible - #until the context exits - print azog - #None - - #on the other hand, BulkMutators can effect data mutated by the - #dataset during the context - d.insert(('hobbit','root','hobbit','mary'), dict(underrated=True)) - - b.delete('hobbit','root','hobbit','mary') - - for i in range(0,100): - b.insert(('orc','root','orc'), dict(is_alive=True)) - - -nameless_orc_army = b.result() - -mary = d.get('hobbit','root','hobbit','mary') -print mary -#None - -#also note that result() does not include keys with user- -#set names or ids -print ('orc','root','orc','azog') in nameless_orc_army -#False - - -""" -Transactions are objects that behave very similar to BatchMutators -however, they also have the accessor methods from Dataset -(and behave like transactions...) -""" - -d.insert(('ent','root','ent','treebeard'), dict(age=9001)) - -t = d.transaction() - -treebeard_props = d.get('ent','root','ent','treebeard') -treebeard_props['age'] += 1 -d.update(('ent','root','ent','treebeard'), treebeard_props) - -# t takes a snapshot of d, so changes made to d after t is -# initialized will not be visible inside the context - - - -with t: - - d.upsert(('hobbit','root','hobbit','frodo'), dict(underrated=False)) - #additionally, changes made while in the context will not be visible - #to transactional reads like t.get() or t.query() - - frodo = t.get('hobbit','root','hobbit','frodo') - print frodo - #None - - #if you need to get automatically allocate a key to mutate later in - #a transaction, you can call t.allocate_id(*key) - - important_orc_key = t.allocate_id('orc','root','orc') - t.upsert(important_orc_key, dict(carrying="explosives!", is_alive=True)) - - - treebeard_props = t.get('ent','root','ent','treebeard') - #t.get() gets 'younger' treebeard - treebeard_props['age'] += 1 - d.update(('ent','root','ent','treebeard'), treebeard_props) - -treebeard_props = d.get('ent','root','ent', 'treebeard') - -print treebeard_props['age'] -#9002 - - -""" -Using GQL is more consistent with the built-in-style of the library -Query syntax uses ordered arguments as the number-args, -and keyword arguments as the named-args, as well as a special arg -called "cursor" which is used for fetching pages -""" - -query_string = 'SELECT __key__ FROM @kind OFFSET @cursor WHERE HAS ANCESTOR @1 AND @2=@3' - -alive_orcs, more = d.query( - query_string, - 'KEY(\'orc\', \'root\')', - 'is_alive', - True, - kind='orc' - cursor=None) - -while(more): - more_alive_orcs, more = d.query( - query_string, - 'KEY(\'orc\', \'root\')', - 'is_alive', - True, - kind='orc' - cursor=more) - - alive_orcs.extend(more_alive_orcs) - -print ('orc', 'root', 'orc', 'azog') in alive_orcs -#True - -print important_orc_key in alive_orcs -#True +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pragma NO COVER +from code import interact +import itertools +import os.path +import sys +import time +from six.moves import input + + +class DemoRunner(object): + """An interactive runner of demo scripts.""" + + KEYPRESS_DELAY = 0.05 + GLOBALS, LOCALS = globals(), locals() + CODE, COMMENT = 'code', 'comment' + + def __init__(self, fp): + self.lines = [line.rstrip() for line in fp.readlines()] + + @classmethod + def from_module(cls, module): + path = os.path.join(os.path.dirname(module.__file__), + 'demo', 'demo.py') + + return cls(open(path, 'r')) + + def run(self): + line_groups = itertools.groupby(self.lines, self.get_line_type) + + for group_type, lines in line_groups: + if group_type == self.COMMENT: + self.write(lines) + + elif group_type == self.CODE: + self.code(lines) + + interact('(Hit CTRL-D to exit...)', local=self.LOCALS) + + def wait(self): + input() + + @classmethod + def get_line_type(cls, line): + if line.startswith('#'): + return cls.COMMENT + else: + return cls.CODE + + def get_indent_level(self, line): + if not line.strip(): + return None + return len(line) - len(line.lstrip()) + + def _print(self, text='', newline=True): + sys.stdout.write(text) + if newline: + sys.stdout.write('\n') + + def write(self, lines): + self._print() + self._print('\n'.join(lines), False) + self.wait() + + def code(self, lines): + code_lines = [] + + for line in lines: + indent = self.get_indent_level(line) + + # If we've completed a block, + # run whatever code was built up in code_lines. + if indent == 0: + self._execute_lines(code_lines) + code_lines = [] + + # Print the prefix for the line depending on the indentation level. + if indent == 0: + self._print('>>> ', False) + elif indent > 0: + self._print('\n... ', False) + elif indent is None: + continue + + # Break the line into the code section and the comment section. + if '#' in line: + code, comment = line.split('#', 2) + else: + code, comment = line, None + + # 'Type' out the comment section. + for char in code.rstrip(): + time.sleep(self.KEYPRESS_DELAY) + sys.stdout.write(char) + sys.stdout.flush() + + # Print the comment section (not typed out). + if comment: + sys.stdout.write(' # %s' % comment.strip()) + + # Add the current line to the list of lines to be run + # in this block. + code_lines.append(line) + + # If we had any code built up that wasn't part of a completed block + # (ie, the lines ended with an indented line), + # run that code. + if code_lines: + self._execute_lines(code_lines) + + def _execute_lines(self, lines): + if lines: + self.wait() + + # Yes, this is crazy unsafe... but it's demo code. + exec('\n'.join(lines), self.GLOBALS, self.LOCALS) From 8ca9aa83757e12f05fd15b418cc6b672d4e368b1 Mon Sep 17 00:00:00 2001 From: Eli Bixby Date: Thu, 18 Dec 2014 11:32:05 -0800 Subject: [PATCH 3/4] Moved new demo to separate file --- gcloud/datastore/demo/{demo.py => demo_new.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename gcloud/datastore/demo/{demo.py => demo_new.py} (100%) diff --git a/gcloud/datastore/demo/demo.py b/gcloud/datastore/demo/demo_new.py similarity index 100% rename from gcloud/datastore/demo/demo.py rename to gcloud/datastore/demo/demo_new.py From 16441c32a9bc67376c92a5b8723ec21e01aa9249 Mon Sep 17 00:00:00 2001 From: Eli Bixby Date: Thu, 18 Dec 2014 12:28:03 -0800 Subject: [PATCH 4/4] Readded original demo, no need to remove --- gcloud/datastore/demo/demo.py | 96 +++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 gcloud/datastore/demo/demo.py diff --git a/gcloud/datastore/demo/demo.py b/gcloud/datastore/demo/demo.py new file mode 100644 index 000000000000..47e600699f21 --- /dev/null +++ b/gcloud/datastore/demo/demo.py @@ -0,0 +1,96 @@ +# Copyright 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pragma NO COVER +# Welcome to the gCloud Datastore Demo! (hit enter) + +# We're going to walk through some of the basics... +# Don't worry though. You don't need to do anything, just keep hitting enter... + +# Let's start by importing the demo module and getting a dataset: +from gcloud.datastore import demo +dataset = demo.get_dataset() + +# Let's create a new entity of type "Thing" and name it 'Toy': +toy = dataset.entity('Thing') +toy.update({'name': 'Toy'}) + +# Now let's save it to our datastore: +toy.save() + +# If we look it up by its key, we should find it... +print(dataset.get_entities([toy.key()])) + +# And we should be able to delete it... +toy.delete() + +# Since we deleted it, if we do another lookup it shouldn't be there again: +print(dataset.get_entities([toy.key()])) + +# Now let's try a more advanced query. +# We'll start by look at all Thing entities: +query = dataset.query().kind('Thing') + +# Let's look at the first two. +print(query.limit(2).fetch()) + +# Now let's check for Thing entities named 'Computer' +print(query.filter('name', '=', 'Computer').fetch()) + +# If you want to filter by multiple attributes, +# you can string .filter() calls together. +print(query.filter('name', '=', 'Computer').filter('age', '=', 10).fetch()) + +# You can also work inside a transaction. +# (Check the official docs for explanations of what's happening here.) +with dataset.transaction(): + print('Creating and savng an entity...') + thing = dataset.entity('Thing') + thing.key(thing.key().name('foo')) + thing['age'] = 10 + thing.save() + + print('Creating and saving another entity...') + thing2 = dataset.entity('Thing') + thing2.key(thing2.key().name('bar')) + thing2['age'] = 15 + thing2.save() + + print('Committing the transaction...') + +# Now that the transaction is commited, let's delete the entities. +print(thing.delete(), thing2.delete()) + +# To rollback a transaction, just call .rollback() +with dataset.transaction() as t: + thing = dataset.entity('Thing') + thing.key(thing.key().name('another')) + thing.save() + t.rollback() + +# Let's check if the entity was actually created: +created = dataset.get_entities([thing.key()]) +print('yes' if created else 'no') + +# Remember, a key won't be complete until the transaction is commited. +# That is, while inside the transaction block, thing.key() will be incomplete. +with dataset.transaction(): + thing = dataset.entity('Thing') + thing.save() + print(thing.key()) # This will be partial + +print(thing.key()) # This will be complete + +# Now let's delete the entity. +thing.delete()