forked from loafle/openapi-generator-original
[python] Add option to return None instead of raising exception when accessing unset attribute (#7784)
* add option to return None instead of raising exception when accessing unset attribute * update python samples * reimplement getattr using getitem or get depending on attrNoneIfUnset * move getattr and setattr to respective templates * update docstrings, def get/setattr methods to have docstrings in them, use __dict__ to avoid recursion issues * revert required_properties change * add manual tests for .get method
This commit is contained in:
@@ -14,6 +14,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|packageUrl|python package URL.| |null|
|
||||
|packageVersion|python package version.| |1.0.0|
|
||||
|projectName|python project name in setup.py (e.g. petstore-api).| |null|
|
||||
|pythonAttrNoneIfUnset|when accessing unset attribute, return `None` instead of raising `ApiAttributeError`| |false|
|
||||
|recursionLimit|Set the recursion limit. If not set, use the system default value.| |null|
|
||||
|useNose|use the nose test framework| |false|
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ public class CodegenConstants {
|
||||
public static final String PYTHON_PACKAGE_NAME = "pythonPackageName";
|
||||
public static final String PYTHON_PACKAGE_NAME_DESC = "package name for generated python code";
|
||||
|
||||
public static final String PYTHON_ATTR_NONE_IF_UNSET = "pythonAttrNoneIfUnset";
|
||||
public static final String PYTHON_ATTR_NONE_IF_UNSET_DESC = "when accessing unset attribute, return `None` instead of raising `ApiAttributeError`";
|
||||
|
||||
public static final String WITH_GO_CODEGEN_COMMENT = "withGoCodegenComment";
|
||||
public static final String WITH_GO_CODEGEN_COMMENT_DESC = "whether to include Go codegen comment to disable Go Lint and collapse by default in GitHub PRs and diffs";
|
||||
|
||||
|
||||
@@ -122,6 +122,9 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
||||
// optional params/props with **kwargs in python
|
||||
cliOptions.remove(4);
|
||||
|
||||
cliOptions.add(new CliOption(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET, CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET_DESC)
|
||||
.defaultValue(Boolean.FALSE.toString()));
|
||||
|
||||
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
|
||||
.stability(Stability.EXPERIMENTAL)
|
||||
.build();
|
||||
@@ -209,6 +212,12 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen {
|
||||
// default this to true so the python ModelSimple models will be generated
|
||||
ModelUtils.setGenerateAliasAsModel(true);
|
||||
LOGGER.info(CodegenConstants.GENERATE_ALIAS_AS_MODEL + " is hard coded to true in this generator. Alias models will only be generated if they contain validations or enums");
|
||||
|
||||
Boolean attrNoneIfUnset = false;
|
||||
if (additionalProperties.containsKey(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET)) {
|
||||
attrNoneIfUnset = Boolean.valueOf(additionalProperties.get(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET).toString());
|
||||
}
|
||||
additionalProperties.put("attrNoneIfUnset", attrNoneIfUnset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
@@ -20,28 +20,22 @@
|
||||
)
|
||||
return None
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
__unset_attribute_value__ = object()
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
# get the attribute from the correct instance
|
||||
model_instances = self._var_name_to_model_instances.get(
|
||||
name, self._additional_properties_model_instances)
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
values = []
|
||||
# A composed model stores child (oneof/anyOf/allOf) models under
|
||||
# self._var_name_to_model_instances. A named property can exist in
|
||||
@@ -55,11 +49,7 @@
|
||||
values.append(v)
|
||||
len_values = len(values)
|
||||
if len_values == 0:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
)
|
||||
return default
|
||||
elif len_values == 1:
|
||||
return values[0]
|
||||
elif len_values > 1:
|
||||
@@ -67,11 +57,22 @@
|
||||
"Values stored for property {0} in {1} differ when looking "
|
||||
"at self and self's composed instances. All values must be "
|
||||
"the same".format(name, type(self).__name__),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
value = self.get(name, self.__unset_attribute_value__)
|
||||
if value is self.__unset_attribute_value__:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
return value
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
@@ -84,4 +85,4 @@
|
||||
if name in model_instance._data_store:
|
||||
return True
|
||||
|
||||
return False
|
||||
return False
|
||||
@@ -1,32 +1,32 @@
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
return name in self.__dict__['_data_store']
|
||||
@@ -1,15 +1,15 @@
|
||||
def __setitem__(self, name, value):
|
||||
"""this allows us to set values with instance[field_name] = val"""
|
||||
self.__setattr__(name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""this allows us to get a value with val = instance[field_name]"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""For `print` and `pprint`"""
|
||||
return self.to_str()
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Returns true if both objects are not equal"""
|
||||
return not self == other
|
||||
return not self == other
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
"""set the value of an attribute using dot notation: `instance.attr = val`"""
|
||||
self[attr] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""get the value of an attribute using dot notation: `instance.attr`"""
|
||||
return self.{{#attrNoneIfUnset}}get{{/attrNoneIfUnset}}{{^attrNoneIfUnset}}__getitem__{{/attrNoneIfUnset}}(attr)
|
||||
@@ -156,14 +156,6 @@ class OpenApiModel(object):
|
||||
)
|
||||
self.__dict__['_data_store'][name] = value
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
"""this allows us to set values with instance[field_name] = val"""
|
||||
self.__setattr__(name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""this allows us to get a value with val = instance[field_name]"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""For `print` and `pprint`"""
|
||||
return self.to_str()
|
||||
@@ -172,6 +164,14 @@ class OpenApiModel(object):
|
||||
"""Returns true if both objects are not equal"""
|
||||
return not self == other
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
"""set the value of an attribute using dot notation: `instance.attr = val`"""
|
||||
self[attr] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""get the value of an attribute using dot notation: `instance.attr`"""
|
||||
return self.__getitem__(attr)
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# this function uses the discriminator to
|
||||
# pick a new schema/class to instantiate because a discriminator
|
||||
@@ -290,40 +290,39 @@ class ModelSimple(OpenApiModel):
|
||||
"""the parent class of models whose type != object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_str(self):
|
||||
"""Returns the string representation of the model"""
|
||||
return str(self.value)
|
||||
@@ -346,40 +345,39 @@ class ModelNormal(OpenApiModel):
|
||||
"""the parent class of models whose type == object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
@@ -432,8 +430,8 @@ class ModelComposed(OpenApiModel):
|
||||
which contain the value that the key is referring to.
|
||||
"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
@@ -454,28 +452,22 @@ class ModelComposed(OpenApiModel):
|
||||
)
|
||||
return None
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
__unset_attribute_value__ = object()
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
# get the attribute from the correct instance
|
||||
model_instances = self._var_name_to_model_instances.get(
|
||||
name, self._additional_properties_model_instances)
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
values = []
|
||||
# A composed model stores child (oneof/anyOf/allOf) models under
|
||||
# self._var_name_to_model_instances. A named property can exist in
|
||||
@@ -489,11 +481,7 @@ class ModelComposed(OpenApiModel):
|
||||
values.append(v)
|
||||
len_values = len(values)
|
||||
if len_values == 0:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
)
|
||||
return default
|
||||
elif len_values == 1:
|
||||
return values[0]
|
||||
elif len_values > 1:
|
||||
@@ -501,11 +489,22 @@ class ModelComposed(OpenApiModel):
|
||||
"Values stored for property {0} in {1} differ when looking "
|
||||
"at self and self's composed instances. All values must be "
|
||||
"the same".format(name, type(self).__name__),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
value = self.get(name, self.__unset_attribute_value__)
|
||||
if value is self.__unset_attribute_value__:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
return value
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
@@ -520,7 +519,6 @@ class ModelComposed(OpenApiModel):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
|
||||
@@ -156,14 +156,6 @@ class OpenApiModel(object):
|
||||
)
|
||||
self.__dict__['_data_store'][name] = value
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
"""this allows us to set values with instance[field_name] = val"""
|
||||
self.__setattr__(name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""this allows us to get a value with val = instance[field_name]"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""For `print` and `pprint`"""
|
||||
return self.to_str()
|
||||
@@ -172,6 +164,14 @@ class OpenApiModel(object):
|
||||
"""Returns true if both objects are not equal"""
|
||||
return not self == other
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
"""set the value of an attribute using dot notation: `instance.attr = val`"""
|
||||
self[attr] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""get the value of an attribute using dot notation: `instance.attr`"""
|
||||
return self.__getitem__(attr)
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# this function uses the discriminator to
|
||||
# pick a new schema/class to instantiate because a discriminator
|
||||
@@ -290,40 +290,39 @@ class ModelSimple(OpenApiModel):
|
||||
"""the parent class of models whose type != object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_str(self):
|
||||
"""Returns the string representation of the model"""
|
||||
return str(self.value)
|
||||
@@ -346,40 +345,39 @@ class ModelNormal(OpenApiModel):
|
||||
"""the parent class of models whose type == object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
@@ -432,8 +430,8 @@ class ModelComposed(OpenApiModel):
|
||||
which contain the value that the key is referring to.
|
||||
"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
@@ -454,28 +452,22 @@ class ModelComposed(OpenApiModel):
|
||||
)
|
||||
return None
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
__unset_attribute_value__ = object()
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
# get the attribute from the correct instance
|
||||
model_instances = self._var_name_to_model_instances.get(
|
||||
name, self._additional_properties_model_instances)
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
values = []
|
||||
# A composed model stores child (oneof/anyOf/allOf) models under
|
||||
# self._var_name_to_model_instances. A named property can exist in
|
||||
@@ -489,11 +481,7 @@ class ModelComposed(OpenApiModel):
|
||||
values.append(v)
|
||||
len_values = len(values)
|
||||
if len_values == 0:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
)
|
||||
return default
|
||||
elif len_values == 1:
|
||||
return values[0]
|
||||
elif len_values > 1:
|
||||
@@ -501,11 +489,22 @@ class ModelComposed(OpenApiModel):
|
||||
"Values stored for property {0} in {1} differ when looking "
|
||||
"at self and self's composed instances. All values must be "
|
||||
"the same".format(name, type(self).__name__),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
value = self.get(name, self.__unset_attribute_value__)
|
||||
if value is self.__unset_attribute_value__:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
return value
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
@@ -520,7 +519,6 @@ class ModelComposed(OpenApiModel):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
|
||||
@@ -156,14 +156,6 @@ class OpenApiModel(object):
|
||||
)
|
||||
self.__dict__['_data_store'][name] = value
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
"""this allows us to set values with instance[field_name] = val"""
|
||||
self.__setattr__(name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""this allows us to get a value with val = instance[field_name]"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""For `print` and `pprint`"""
|
||||
return self.to_str()
|
||||
@@ -172,6 +164,14 @@ class OpenApiModel(object):
|
||||
"""Returns true if both objects are not equal"""
|
||||
return not self == other
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
"""set the value of an attribute using dot notation: `instance.attr = val`"""
|
||||
self[attr] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""get the value of an attribute using dot notation: `instance.attr`"""
|
||||
return self.__getitem__(attr)
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# this function uses the discriminator to
|
||||
# pick a new schema/class to instantiate because a discriminator
|
||||
@@ -290,40 +290,39 @@ class ModelSimple(OpenApiModel):
|
||||
"""the parent class of models whose type != object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_str(self):
|
||||
"""Returns the string representation of the model"""
|
||||
return str(self.value)
|
||||
@@ -346,40 +345,39 @@ class ModelNormal(OpenApiModel):
|
||||
"""the parent class of models whose type == object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
@@ -432,8 +430,8 @@ class ModelComposed(OpenApiModel):
|
||||
which contain the value that the key is referring to.
|
||||
"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
@@ -454,28 +452,22 @@ class ModelComposed(OpenApiModel):
|
||||
)
|
||||
return None
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
__unset_attribute_value__ = object()
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
# get the attribute from the correct instance
|
||||
model_instances = self._var_name_to_model_instances.get(
|
||||
name, self._additional_properties_model_instances)
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
values = []
|
||||
# A composed model stores child (oneof/anyOf/allOf) models under
|
||||
# self._var_name_to_model_instances. A named property can exist in
|
||||
@@ -489,11 +481,7 @@ class ModelComposed(OpenApiModel):
|
||||
values.append(v)
|
||||
len_values = len(values)
|
||||
if len_values == 0:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
)
|
||||
return default
|
||||
elif len_values == 1:
|
||||
return values[0]
|
||||
elif len_values > 1:
|
||||
@@ -501,11 +489,22 @@ class ModelComposed(OpenApiModel):
|
||||
"Values stored for property {0} in {1} differ when looking "
|
||||
"at self and self's composed instances. All values must be "
|
||||
"the same".format(name, type(self).__name__),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
value = self.get(name, self.__unset_attribute_value__)
|
||||
if value is self.__unset_attribute_value__:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
return value
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
@@ -520,7 +519,6 @@ class ModelComposed(OpenApiModel):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
|
||||
@@ -156,14 +156,6 @@ class OpenApiModel(object):
|
||||
)
|
||||
self.__dict__['_data_store'][name] = value
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
"""this allows us to set values with instance[field_name] = val"""
|
||||
self.__setattr__(name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""this allows us to get a value with val = instance[field_name]"""
|
||||
return self.__getattr__(name)
|
||||
|
||||
def __repr__(self):
|
||||
"""For `print` and `pprint`"""
|
||||
return self.to_str()
|
||||
@@ -172,6 +164,14 @@ class OpenApiModel(object):
|
||||
"""Returns true if both objects are not equal"""
|
||||
return not self == other
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
"""set the value of an attribute using dot notation: `instance.attr = val`"""
|
||||
self[attr] = value
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""get the value of an attribute using dot notation: `instance.attr`"""
|
||||
return self.__getitem__(attr)
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# this function uses the discriminator to
|
||||
# pick a new schema/class to instantiate because a discriminator
|
||||
@@ -290,40 +290,39 @@ class ModelSimple(OpenApiModel):
|
||||
"""the parent class of models whose type != object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_str(self):
|
||||
"""Returns the string representation of the model"""
|
||||
return str(self.value)
|
||||
@@ -346,40 +345,39 @@ class ModelNormal(OpenApiModel):
|
||||
"""the parent class of models whose type == object in their
|
||||
swagger/openapi"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
|
||||
self.set_attribute(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
if name in self.__dict__['_data_store']:
|
||||
return self.__dict__['_data_store'][name]
|
||||
return self.__dict__['_data_store'].get(name, default)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
if name in self:
|
||||
return self.get(name)
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[name]
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
|
||||
return name in self.__dict__['_data_store']
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
@@ -432,8 +430,8 @@ class ModelComposed(OpenApiModel):
|
||||
which contain the value that the key is referring to.
|
||||
"""
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
"""this allows us to set a value with instance.field_name = val"""
|
||||
def __setitem__(self, name, value):
|
||||
"""set the value of an attribute using square-bracket notation: `instance[attr] = val`"""
|
||||
if name in self.required_properties:
|
||||
self.__dict__[name] = value
|
||||
return
|
||||
@@ -454,28 +452,22 @@ class ModelComposed(OpenApiModel):
|
||||
)
|
||||
return None
|
||||
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""this allows us to get a value with val = instance.field_name"""
|
||||
__unset_attribute_value__ = object()
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""returns the value of an attribute or some default value if the attribute was not set"""
|
||||
if name in self.required_properties:
|
||||
return self.__dict__[name]
|
||||
|
||||
# get the attribute from the correct instance
|
||||
model_instances = self._var_name_to_model_instances.get(
|
||||
name, self._additional_properties_model_instances)
|
||||
path_to_item = []
|
||||
if self._path_to_item:
|
||||
path_to_item.extend(self._path_to_item)
|
||||
path_to_item.append(name)
|
||||
values = []
|
||||
# A composed model stores child (oneof/anyOf/allOf) models under
|
||||
# self._var_name_to_model_instances. A named property can exist in
|
||||
@@ -489,11 +481,7 @@ class ModelComposed(OpenApiModel):
|
||||
values.append(v)
|
||||
len_values = len(values)
|
||||
if len_values == 0:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
path_to_item
|
||||
)
|
||||
return default
|
||||
elif len_values == 1:
|
||||
return values[0]
|
||||
elif len_values > 1:
|
||||
@@ -501,11 +489,22 @@ class ModelComposed(OpenApiModel):
|
||||
"Values stored for property {0} in {1} differ when looking "
|
||||
"at self and self's composed instances. All values must be "
|
||||
"the same".format(name, type(self).__name__),
|
||||
path_to_item
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""get the value of an attribute using square-bracket notation: `instance[attr]`"""
|
||||
value = self.get(name, self.__unset_attribute_value__)
|
||||
if value is self.__unset_attribute_value__:
|
||||
raise ApiAttributeError(
|
||||
"{0} has no attribute '{1}'".format(
|
||||
type(self).__name__, name),
|
||||
[e for e in [self._path_to_item, name] if e]
|
||||
)
|
||||
return value
|
||||
|
||||
def __contains__(self, name):
|
||||
"""this allows us to use `in` operator: `'attr' in instance`"""
|
||||
"""used by `in` operator to check if an attrbute value was set in an instance: `'attr' in instance`"""
|
||||
|
||||
if name in self.required_properties:
|
||||
return name in self.__dict__
|
||||
@@ -520,7 +519,6 @@ class ModelComposed(OpenApiModel):
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns the model properties as a dict"""
|
||||
return model_to_dict(self, serialize=False)
|
||||
|
||||
@@ -47,6 +47,7 @@ class TestFruit(unittest.TestCase):
|
||||
# check its properties
|
||||
self.assertEqual(fruit.length_cm, length_cm)
|
||||
self.assertEqual(fruit['length_cm'], length_cm)
|
||||
self.assertEqual(fruit.get('length_cm'), length_cm)
|
||||
self.assertEqual(getattr(fruit, 'length_cm'), length_cm)
|
||||
self.assertEqual(fruit.color, color)
|
||||
self.assertEqual(fruit['color'], color)
|
||||
@@ -85,6 +86,8 @@ class TestFruit(unittest.TestCase):
|
||||
# Per Python doc, if the named attribute does not exist,
|
||||
# default is returned if provided.
|
||||
self.assertEqual(getattr(fruit, 'cultivar', 'some value'), 'some value')
|
||||
self.assertEqual(fruit.get('cultivar'), None)
|
||||
self.assertEqual(fruit.get('cultivar', 'some value'), 'some value')
|
||||
|
||||
# Per Python doc, if the named attribute does not exist,
|
||||
# default is returned if provided, otherwise AttributeError is raised.
|
||||
|
||||
Reference in New Issue
Block a user