Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smooth out API surfaces before M1 #336

Closed
dhermes opened this issue Nov 4, 2014 · 21 comments
Closed

Smooth out API surfaces before M1 #336

dhermes opened this issue Nov 4, 2014 · 21 comments
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.

Comments

@dhermes
Copy link
Contributor

dhermes commented Nov 4, 2014

Current offenders:

  1. There is no way to allocate_ids without explicitly using protobufs. I have partially addressed (Implemented in Adding a non-protobuf allocate_ids method on Dataset. #436).

  2. Transactions don't give any indication of success or failure (i.e. commit or rollback in __exit__). (Filed DISCUSSION: Is there a way for a Transaction to indicate success/failure from __exit__ #496)

  3. Query (and other classes) could be more Pythonic by allowing us to pass values to constructor that also have their own setters. For example:

    query = dataset.query('Character').ancestor(
        ANCESTOR_KEY).limit(10).order('appearances')

    becomes

    query = dataset.query('Character', ancestor=ANCESTOR_KEY,
                          limit=10, order='appearances')

    We don't have to go as extreme as DISCUSSION: Are setter methods which clone and return object type confusing to end-users? #188 by wiping away the return self behavior in the setters, but could use them in conjuction with more Pythonic constructors. (Implemented in Fix #439: split query vs. iterator apart. #487)

  4. Query.cursor() should not raise an error before being used. Using query.cursor() is None gives a way to check progress in a query. For example, in paging: (addressed in Removing cursor and more results from query object. #432)

    query = dataset.query(kind=kind).limit(FETCH_MAX)
    results = []
    more_results = True
    while more_results:
        # Make new query.
        if query._cursor is not None:
            query = query.with_cursor(query._cursor)
    
        curr_results = query.fetch()
        results.extend(curr_results)
    
        more_results = len(curr_results) == FETCH_MAX
  5. Query should give access to start_cursor and end_cursor. It is currently only set on the pb. (see Removing cursor and more results from query object. #432)

  6. Comparing Entity values should be easier. Current workaround is

    arya_dict = dict(arya_entity.items())
    self.assertEqual(arya_dict, {'name': 'Arya', 'family': 'Stark'})
  7. Paging with offsets should be a bit more automatic, as it is in gcloud-node. Currently we have to

    query = query.offset(offset).limit(limit)
    entities = page_query.fetch()
    # Offset needs to be reset to 0 since we want to start exactly at `cursor`.
    next_query = query.with_cursor(cursor).offset(0)

    (done in Fix #439: split query vs. iterator apart. #487, since offset always defaults to 0)

  8. I'm sure there will be plenty more to come after Implementing storage regression tests to match gcloud-node. #319

  9. (UPDATED): Consider making bulk-adding properties to Entity more intuitive. See update() override for datastore entities #389. (dict.update() is Pythonic and simple to use / multi-type signature)

  10. (UPDATED): "Creating a new datastore.entity with a key name is a pain." See Creating a new datastore.entity with a key name is a pain #82

@dhermes dhermes added milestone-feature type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. labels Nov 4, 2014
@dhermes dhermes added this to the M1: core, datastore & storage milestone Nov 11, 2014
@dhermes
Copy link
Contributor Author

dhermes commented Nov 12, 2014

Suggestion from @tseaver (paraphrased):

We might extend Query to accomodate a fetch_keys() which handles setting the
projection and then returning only the Keys (instead of the Entitys that were returned)
in the projection='__key__' query.

dhermes added a commit to dhermes/google-cloud-python that referenced this issue Dec 18, 2014
dhermes added a commit to dhermes/google-cloud-python that referenced this issue Dec 18, 2014
dhermes added a commit to dhermes/google-cloud-python that referenced this issue Dec 18, 2014
dhermes added a commit to dhermes/google-cloud-python that referenced this issue Dec 18, 2014
dhermes added a commit to dhermes/google-cloud-python that referenced this issue Dec 18, 2014
@elibixby
Copy link

I recently wrote https://github.com/GoogleCloudPlatform/gcloud-python-todos/tree/precodereview

I'll try to contribute some bite-sized feedback here =)

The first thing is I'd like to be able to send json data to the datastore without having to learn a new class. In the case of:

d = dataset()
key = Key.from_path('todo','root','todo',id)
data = json.loads(request.data)
#do stuff with data
entity.from_key(key, dataset=d)
entity.update(data)
entity.save()

I would rather just call

data = json.loads(request.data)
#do stuff with data
d.put(('todo','root','todo','id), data)

@dhermes
Copy link
Contributor Author

dhermes commented Dec 18, 2014

@elibixby This was somewhat part of the discussion in #410.

I think a happy middle ground would be:

data = json.loads(request.data)
# do stuff
entity = datastore.Entity(key=('todo', 'root', 'todo', 'id), data=data)
entity.save()

This somewhat fits behind the discussion we previously had (with @elibixby's previous account) in #396 which seems to have fallen by the wayside.


In general, sophisticated applications would likely prefer an ORM, so this is kind of moot in some sense.

I'd prefer to have a high-quality mapping to the API and then begin a process of making ndb work on top of it. I tried to articulate this in a previous discussion.

This also means making a "getting started" app would be for people that build libraries rather than people that use libraries. It now occurs to me that @proppy did this with the googledatastore library and I'm curious if that is being actively maintained.

If it is, we need to coordinate and take the mix of three (datastore package here, googledatastore and ndb) and find a path to make it two.

@dhermes
Copy link
Contributor Author

dhermes commented Dec 18, 2014

Asking users to carry around untyped data and keep track of what it all means is a tall task to do well. By providing an Entity class which is not only a data container but also has methods for interacting with the API (save, reload, delete) we provide a consistent way to do easy things and to do complex things.

Jack's talk is good (I just re-watched it); adding complexity is not always a good thing. But here the class allows for doing simple things easily (though as noted above, it could be easier) as well as migrating nicely from a simple "hello world" use case to more complex ones.

(FWIW I'm not the creator of this class and am open to other approaches, though the codebase is fairly mature and throwing it all away seems the wrong move.)

@elibixby
Copy link

Totally agree about the two library thing. I think the point I'm trying to make is that those two libraries should be far apart in usage in order to best support the user-base.

I understood gcloud-python as a library facing developers of applications, rather than developers of libraries. If we were two develop two libraries on-top of gcloud-python, one which is more or less classless and one which is a full-blown ORM that would make more sense to me.

@elibixby
Copy link

One option that would be not totally classless but not use entity is to keep a Key class around but not the entity.

so something like

dataset.put(Key('todo','root','todo',id), data)

EDIT: fundamentally it doesn't feel like there is much of an internal state, or immutable objecty-ness tied up in the entity class. Keys are a bit more complex and have some formatting defined on them. Sorry I'm not being so helpful/constructive. Having played with the library a bit, it's harder than I expected to make my frustrations concrete.

@dhermes
Copy link
Contributor Author

dhermes commented Dec 18, 2014

@elibixby It's clear your preference is a simple interface that doesn't require much other than the data itself (which must adhere to simple proto types like int, string, bytes, etc.).

It's not clear to me that this is a general thing that people want or expect and I hesitate to make such a large decision based on such a small sample size.

ISTM that by just carrying around the basic types, we provide no way to iterate on or update data through the lifetime of an application. Instead, that burden falls completely on our users, which doesn't seem like the right answer.

@elibixby
Copy link

I understand that perspective. I think in that case, that Entity having a mutable key breaks the illusion of consistency. Although it would be a terrible practice a user could theoretically change an entity object into a completely different entity object by saying:

entity = dataset.Entity(key=old_key, data)
entity.key(new_key)
entity.update()
#confusion ensues

This ties back in with your comment about idiomatic libraries not just encouraging good practice, but enforcing it.

@dhermes
Copy link
Contributor Author

dhermes commented Dec 18, 2014

Yes the immutability of an Entity key was posed (by the original author and @proppy) in #3, one of the earliest questions, and is still unanswered. You may be right that it should return a new Entity just like most other setters do.

Or instead we should disallow changing the key ever and enable copying data over to a new Entity in a simple way.

@elibixby
Copy link

It does seem like there are no good options there ='(

@proppy
Copy link
Contributor

proppy commented Dec 18, 2014

I think the analysis about the layering of library is spot-on, I can think of the following level:
0/ lowest level API that only expose a dataset type, with one method per RPC call that takes proto, ideally generated
1/ low level API that factor those methods into python idioms, works well with basic type and is flexible and non intrusive with existing codebase
2/ dynamic model API similar to the current Entity class or NDB expando model, doesn't enforce any schema but provide base object that you subclass when targeting datastore first.
3/ a full blown ORM, with user defined property schema on Model like NDB

that burden falls completely on our users, which doesn't seem like the right answer.

I think it's also worth noting that some existing application might already have some existing data model for their app, with its own hierarchy of classes. And that this model is very likely to already have a way to serialized itself to a dict, should they need to send it over somewhere else in JSON.

Asking application to subclass Entity in order to store anything in the datastore is intrusive for those kind of application. And creating the Entity object on the fly seems noisy compared to just pass a dict variable around.

/cc @pcostell

@elibixby
Copy link

Would it be too abnormal to build immutability into the Key class except in the case of a onetime transition from partial key to full key?

entity = dataset.entity(Key('a','partial','key'), data)
entity.key.name = 'name'
entity.key.name = 'blah'
#fails

Just a thought, there is probably much more idiomatic syntax out there for it. But making keys immutable seems to make a lot of sense except in this one case.

@dhermes
Copy link
Contributor Author

dhermes commented Dec 18, 2014

There's no doubt that keys and the other types need a clearer contract to enable valid __eq__ methods to be defined (discussed in #293) or even better for == to "just work" without having to define anything.

I think non-idempotent operations like

entity.key.name = 'name'
entity.key.name = 'blah'

surprise and (don't) amuse the user, but maybe that can lead to somewhere useful.

@elibixby
Copy link

The presence of _dataset_key is one of the major reasons I was confused as to why you need to attach a key to an entity, and an entity to a dataset, and following that, why you need to have an entity at all.

Either a Key is really just a wrapper for a path, or it is all of the information needed to locate an object in the datastore, and includes dataset_id and namespace. I lean towards the latter, which is also what pushes me towards a put(Key, dict) syntax.

@dhermes
Copy link
Contributor Author

dhermes commented Jan 12, 2015

Closing this out as all have been addressed. Last (not so important) placed in #533

@dhermes dhermes closed this as completed Jan 12, 2015
urshala pushed a commit to urshala/google-cloud-python that referenced this issue Jan 17, 2020
atulep pushed a commit that referenced this issue Apr 3, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
atulep pushed a commit that referenced this issue Apr 6, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
atulep pushed a commit that referenced this issue Apr 6, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
atulep pushed a commit that referenced this issue Apr 18, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
parthea added a commit that referenced this issue Jun 4, 2023
Co-authored-by: Anthonios Partheniou <partheniou@google.com>
parthea pushed a commit that referenced this issue Jun 4, 2023
Samples migration to python-docs-samples in scope of monorepo migration
parthea pushed a commit that referenced this issue Jun 4, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Jun 4, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
parthea added a commit that referenced this issue Jul 6, 2023
* chore(python): drop python 3.6

Source-Link: googleapis/synthtool@4f89b13
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:e7bb19d47c13839fe8c147e50e02e8b6cf5da8edd1af8b82208cd6f66cc2829c

* add api_description to .repo-metadata.json

* require python 3.7+ in setup.py

* remove python 3.6 sample configs

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: Anthonios Partheniou <partheniou@google.com>
parthea added a commit that referenced this issue Sep 20, 2023
Co-authored-by: Anthonios Partheniou <partheniou@google.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
Source-Link: https://github.com/googleapis/synthtool/commit/0ddbff8012e47cde4462fe3f9feab01fbc4cdfd6
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:bced5ca77c4dda0fd2f5d845d4035fc3c5d3d6b81f245246a36aee114970082b
parthea pushed a commit that referenced this issue Sep 22, 2023
* chore: Update gapic-generator-python to v1.8.4

PiperOrigin-RevId: 507808936

Source-Link: googleapis/googleapis@64cf849

Source-Link: googleapis/googleapis-gen@53c48ca
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNTNjNDhjYWMxNTNkM2IzN2YzZDJjMmRlYzQ4MzBjZmQ5MWVjNDE1MyJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
* docs: Add documentation for enums

fix: Add context manager return types

chore: Update gapic-generator-python to v1.8.1
PiperOrigin-RevId: 503210727

Source-Link: googleapis/googleapis@a391fd1

Source-Link: googleapis/googleapis-gen@0080f83
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMDA4MGY4MzBkZWMzN2MzMzg0MTU3MDgyYmNlMjc5ZTM3MDc5ZWE1OCJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
* chore: upgrade gapic-generator-java, gax-java and gapic-generator-python

PiperOrigin-RevId: 423842556

Source-Link: googleapis/googleapis@a616ca0

Source-Link: googleapis/googleapis-gen@29b938c
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjliOTM4YzU4YzFlNTFkMDE5ZjJlZTUzOWQ1NWRjMGEzYzg2YTkwNSJ9

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
Source-Link: googleapis/synthtool@571ee2c
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:09af371bb7d8ebbaef620bfc76c0a3a42da96d75f4821409b54f3466d4ebbd3c

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
* feat: enable "rest" transport in Python for services supporting numeric enums

PiperOrigin-RevId: 508143576

Source-Link: googleapis/googleapis@7a702a9

Source-Link: googleapis/googleapis-gen@6ad1279
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNmFkMTI3OWMwZTdhYTc4N2FjNmI2NmM5ZmQ0YTIxMDY5MmVkZmZjZCJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* chore: Update gapic-generator-python to v1.8.5

PiperOrigin-RevId: 511892190

Source-Link: googleapis/googleapis@a45d9c0

Source-Link: googleapis/googleapis-gen@1907294
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMTkwNzI5NGIxZDgzNjVlYTI0ZjhjNWYyZTA1OWE2NDEyNGM0ZWQzYiJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: Victor Chudnovsky <vchudnov@google.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Sep 22, 2023
Source-Link: googleapis/synthtool@d2871d9
Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:b2dc5f80edcf5d4486c39068c9fa11f7f851d9568eea4dcba130f994ea9b5e97

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Oct 21, 2023
* chore: use gapic-generator-python 0.63.2
docs: add generated snippets

PiperOrigin-RevId: 427792504

Source-Link: googleapis/googleapis@55b9e1e

Source-Link: googleapis/googleapis-gen@bf4e86b
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiYmY0ZTg2Yjc1M2Y0MmNiMGVkYjFmZDUxZmJlODQwZDdkYTBhMWNkZSJ9

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* chore: use gapic-generator-python 0.63.4

chore: fix snippet region tag format
chore: fix docstring code block formatting
PiperOrigin-RevId: 430730865

Source-Link: googleapis/googleapis@ea58002

Source-Link: googleapis/googleapis-gen@ca893ff
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiY2E4OTNmZjhhZjI1ZmM3ZmUwMDFkZTE0MDVhNTE3ZDgwNDQ2ZWNjYSJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
Co-authored-by: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com>
parthea added a commit that referenced this issue Oct 21, 2023
* chore: configure release-please on previous major versions

* chore: use latest owlbot image

Co-authored-by: Anthonios Partheniou <partheniou@google.com>
parthea pushed a commit that referenced this issue Oct 21, 2023
… and property resources) (#336)

* feat: add `RunAccessReport` method (with bindings for account and property resources) to the Admin API v1beta
feat: add `AccessDimension`, `AccessMetric`, `AccessDateRange`, `AccessFilterExpression`, `AccessFilterExpressionList`, `AccessFilter`, `AccessStringFilter`, `AccessInListFilter`, `AccessNumericFilter`, `AccessBetweenFilter`, `NumericValue`, `AccessOrderBy`, `AccessDimensionHeader`, `AccessMetricHeader`, `AccessRow`, `AccessDimensionValue`, `AccessMetricValue`, `AccessQuota`, `AccessQuotaStatus` types to the Admin API v1beta

PiperOrigin-RevId: 519058583

Source-Link: googleapis/googleapis@71bd02e

Source-Link: googleapis/googleapis-gen@5d4f0f6
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNWQ0ZjBmNmI0NTkyM2Q2MmM3ZWVjNjU2YmFjYzNmMDYzM2UwMTFmNyJ9

* 🦉 Updates from OwlBot post-processor

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

---------

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
parthea pushed a commit that referenced this issue Oct 21, 2023
- [ ] Regenerate this pull request now.

PiperOrigin-RevId: 472561635

Source-Link: googleapis/googleapis@332ecf5

Source-Link: googleapis/googleapis-gen@4313d68
Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNDMxM2Q2ODI4ODBmZDlkNzI0NzI5MTE2NGQ0ZTlkM2Q1YmQ5ZjE3NyJ9
parthea pushed a commit that referenced this issue Oct 22, 2023
Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

No branches or pull requests

4 participants