@@ -59,12 +59,18 @@ def __init__(self, *path_args, **kwargs):
5959 :type dataset_id: string
6060 :param dataset_id: The dataset ID associated with the key. This is
6161 required. Can only be passed as a keyword argument.
62+
63+ :type parent: :class:`gcloud.datastore.key.Key`
64+ :param parent: The parent of the key. Can only be passed as a
65+ keyword argument.
6266 """
63- self ._path = self ._parse_path (path_args )
6467 self ._flat_path = path_args
65- self ._parent = None
68+ self ._parent = kwargs . get ( 'parent' )
6669 self ._namespace = kwargs .get ('namespace' )
6770 self ._dataset_id = kwargs .get ('dataset_id' )
71+ # _flat_path, _parent, _namespace and _dataset_id must be set before
72+ # _combine_args() is called.
73+ self ._path = self ._combine_args ()
6874 self ._validate_dataset_id ()
6975
7076 def _validate_dataset_id (self ):
@@ -86,6 +92,11 @@ def _validate_dataset_id(self):
8692 def _parse_path (path_args ):
8793 """Parses positional arguments into key path with kinds and IDs.
8894
95+ :type path_args: :class:`tuple`
96+ :param path_args: A tuple from positional arguments. Should be
97+ alternating list of kinds (string) and id/name
98+ parts (int or string).
99+
89100 :rtype: list of dict
90101 :returns: A list of key parts with kind and id or name set.
91102 :raises: `ValueError` if there are no `path_args`, if one of the
@@ -121,17 +132,42 @@ def _parse_path(path_args):
121132
122133 return result
123134
135+ def _combine_args (self ):
136+ """Sets protected data by combining raw data set from the constructor.
137+
138+ If a _parent is set, updates the _flat_path and sets the
139+ _namespace and _dataset_id if not already set.
140+
141+ :rtype: list of dict
142+ :returns: A list of key parts with kind and id or name set.
143+ :raises: `ValueError` if the parent key is not complete.
144+ """
145+ child_path = self ._parse_path (self ._flat_path )
146+
147+ if self ._parent is not None :
148+ if self ._parent .is_partial :
149+ raise ValueError ('Parent key must be complete.' )
150+
151+ # We know that _parent.path() will return a copy.
152+ child_path = self ._parent .path + child_path
153+ self ._flat_path = self ._parent .flat_path + self ._flat_path
154+ self ._namespace = self ._namespace or self ._parent .namespace
155+ self ._dataset_id = self ._dataset_id or self ._parent .dataset_id
156+
157+ return child_path
158+
124159 def _clone (self ):
125160 """Duplicates the Key.
126161
127- We make a shallow copy of the :class:`gcloud.datastore.dataset.Dataset`
128- because it holds a reference an authenticated connection,
129- which we don't want to lose .
162+ Most attributes are simple types, so don't require copying. Other
163+ attributes like `parent` are long-lived and so we re-use them rather
164+ than creating copies .
130165
131166 :rtype: :class:`gcloud.datastore.key.Key`
132- :returns: a new `Key` instance
167+ :returns: A new `Key` instance with the same data as the current one.
133168 """
134- return copy .deepcopy (self )
169+ return Key (* self .flat_path , parent = self .parent ,
170+ dataset_id = self .dataset_id , namespace = self .namespace )
135171
136172 def complete_key (self , id_or_name ):
137173 """Creates new key from existing partial key by adding final ID/name.
0 commit comments