@@ -60,12 +60,18 @@ def __init__(self, *path_args, **kwargs):
6060 :param dataset_id: The dataset ID associated with the key. Required,
6161 unless the implicit dataset ID has been set. Can
6262 only be passed as a keyword argument.
63+
64+ :type parent: :class:`gcloud.datastore.key.Key`
65+ :param parent: The parent of the key. Can only be passed as a
66+ keyword argument.
6367 """
64- self ._path = self ._parse_path (path_args )
6568 self ._flat_path = path_args
66- self ._parent = None
69+ self ._parent = kwargs . get ( 'parent' )
6770 self ._namespace = kwargs .get ('namespace' )
6871 self ._dataset_id = kwargs .get ('dataset_id' )
72+ # _flat_path, _parent, _namespace and _dataset_id must be set before
73+ # _combine_args() is called.
74+ self ._path = self ._combine_args ()
6975 self ._validate_dataset_id ()
7076
7177 def _validate_dataset_id (self ):
@@ -87,6 +93,11 @@ def _validate_dataset_id(self):
8793 def _parse_path (path_args ):
8894 """Parses positional arguments into key path with kinds and IDs.
8995
96+ :type path_args: :class:`tuple`
97+ :param path_args: A tuple from positional arguments. Should be
98+ alternating list of kinds (string) and id/name
99+ parts (int or string).
100+
90101 :rtype: list of dict
91102 :returns: A list of key parts with kind and id or name set.
92103 :raises: `ValueError` if there are no `path_args`, if one of the
@@ -123,17 +134,49 @@ def _parse_path(path_args):
123134
124135 return result
125136
137+ def _combine_args (self ):
138+ """Sets protected data by combining raw data set from the constructor.
139+
140+ If a _parent is set, updates the _flat_path and sets the
141+ _namespace and _dataset_id if not already set.
142+
143+ :rtype: list of dict
144+ :returns: A list of key parts with kind and id or name set.
145+ :raises: `ValueError` if the parent key is not complete.
146+ """
147+ child_path = self ._parse_path (self ._flat_path )
148+
149+ if self ._parent is not None :
150+ if self ._parent .is_partial :
151+ raise ValueError ('Parent key must be complete.' )
152+
153+ # We know that _parent.path() will return a copy.
154+ child_path = self ._parent .path + child_path
155+ self ._flat_path = self ._parent .flat_path + self ._flat_path
156+ if (self ._namespace is not None and
157+ self ._namespace != self ._parent .namespace ):
158+ raise ValueError ('Child namespace must agree with parent\' s.' )
159+ self ._namespace = self ._parent .namespace
160+ if (self ._dataset_id is not None and
161+ self ._dataset_id != self ._parent .dataset_id ):
162+ raise ValueError ('Child dataset ID must agree with parent\' s.' )
163+ self ._dataset_id = self ._parent .dataset_id
164+
165+ return child_path
166+
126167 def _clone (self ):
127168 """Duplicates the Key.
128169
129- We make a shallow copy of the :class:`gcloud.datastore.dataset.Dataset`
130- because it holds a reference an authenticated connection,
131- which we don't want to lose .
170+ Most attributes are simple types, so don't require copying. Other
171+ attributes like `parent` are long-lived and so we re-use them rather
172+ than creating copies .
132173
133174 :rtype: :class:`gcloud.datastore.key.Key`
134- :returns: a new `Key` instance
175+ :returns: A new `Key` instance with the same data as the current one.
135176 """
136- return copy .deepcopy (self )
177+ return self .__class__ (* self .flat_path , parent = self .parent ,
178+ dataset_id = self .dataset_id ,
179+ namespace = self .namespace )
137180
138181 def completed_key (self , id_or_name ):
139182 """Creates new key from existing partial key by adding final ID/name.
@@ -285,8 +328,8 @@ def _make_parent(self):
285328 else :
286329 parent_args = self .flat_path [:- 2 ]
287330 if parent_args :
288- return Key (* parent_args , dataset_id = self .dataset_id ,
289- namespace = self .namespace )
331+ return self . __class__ (* parent_args , dataset_id = self .dataset_id ,
332+ namespace = self .namespace )
290333
291334 @property
292335 def parent (self ):
0 commit comments