Repository: samuelcolvin/pydantic
Pull request #2058 (2020-10-28 10:09:39)

Update datamodel_code_generator.md
Just a quick overhaul of the docs


codecov[bot]: # [Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=h1) Report
> Merging [#2058](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=desc) into [master](https://codecov.io/gh/samuelcolvin/pydantic/commit/95435de4526b609ee904b7a27478cec2e32f8fe8?el=desc) will **decrease** coverage by `0.36%`.
> The diff coverage is `n/a`.


```diff
@@             Coverage Diff             @@
##            master    #2058      +/-   ##
===========================================
- Coverage   100.00%   99.63%   -0.37%     
===========================================
  Files           21       21              
  Lines         4094     4094              
  Branches       821      821              
===========================================
- Hits          4094     4079      -15     
- Misses           0       12      +12     
- Partials         0        3       +3     
```


| [Impacted Files](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pydantic/version.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058/diff?src=pr&el=tree#diff-cHlkYW50aWMvdmVyc2lvbi5weQ==) | `88.23% <0.00%> (-11.77%)` | :arrow_down: |
| [pydantic/typing.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058/diff?src=pr&el=tree#diff-cHlkYW50aWMvdHlwaW5nLnB5) | `94.30% <0.00%> (-5.70%)` | :arrow_down: |
| [pydantic/env\_settings.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058/diff?src=pr&el=tree#diff-cHlkYW50aWMvZW52X3NldHRpbmdzLnB5) | `97.93% <0.00%> (-2.07%)` | :arrow_down: |
| [pydantic/networks.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058/diff?src=pr&el=tree#diff-cHlkYW50aWMvbmV0d29ya3MucHk=) | `99.22% <0.00%> (-0.78%)` | :arrow_down: |
| [pydantic/utils.py](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058/diff?src=pr&el=tree#diff-cHlkYW50aWMvdXRpbHMucHk=) | `99.29% <0.00%> (-0.71%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=footer). Last update [95435de...5e2474a](https://codecov.io/gh/samuelcolvin/pydantic/pull/2058?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/changes/2048-aphedges.md b/changes/2048-aphedges.md
new file mode 100644
index 000000000..0407f2367
--- /dev/null
+++ b/changes/2048-aphedges.md
@@ -0,0 +1,1 @@
+Fix mypy assignment error when using `PrivateAttr`
diff --git a/docs/datamodel_code_generator.md b/docs/datamodel_code_generator.md
index d96a11c28..dec9cbcb5 100644
--- a/docs/datamodel_code_generator.md
+++ b/docs/datamodel_code_generator.md
@@ -1,18 +1,18 @@
-[datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator/) is a command to generate pydantic models from other data types.
+The [datamodel-code-generator](https://github.com/koxudaxi/datamodel-code-generator/) project is a library and command-line utility to generate pydantic models from just about any data source, including:
 
+* OpenAPI 3 (YAML/JSON)
+* JSON Schema
+* JSON/YAML Data (which will converted to JSON Schema)
 
-* Supported source types
-    * OpenAPI 3 (YAML/JSON)
-    * JSON Schema
-    * JSON/YAML Data (It will be converted to JSON Schema)
+Whenever you find yourself with any data convertible JSON but without pydantic models, this tool will allow you to generate type-safe model hierarchies on demand.
 
-## Install
+## Installation
 ```bash
 pip install datamodel-code-generator
 ```
 
 ## Example
-In this case, The datamodel-code-generator creates pydantic models from JSON Schema.
+In this case, datamodel-code-generator creates pydantic models from a JSON Schema file.
 ```bash
 datamodel-codegen  --input person.json --input-file-type jsonschema --output model.py
 ```
diff --git a/pydantic/fields.py b/pydantic/fields.py
index c451770c0..36ea690a2 100644
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -799,21 +799,10 @@ class ModelField(Representation):
         return args
 
 
-class PrivateAttr(Representation):
-    """
-    Indicates that attribute is only used internally and never mixed with regular fields.
-
-    Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant.
-
-    Private attrs are stored in model __slots__.
-    """
-
+class ModelPrivateAttr(Representation):
     __slots__ = ('default', 'default_factory')
 
     def __init__(self, default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None) -> None:
-        if default is not Undefined and default_factory is not None:
-            raise TypeError('default and default_factory args can not be used together')
-
         self.default = default
         self.default_factory = default_factory
 
@@ -825,3 +814,28 @@ class PrivateAttr(Representation):
             other.default,
             other.default_factory,
         )
+
+
+def PrivateAttr(
+    default: Any = Undefined,
+    *,
+    default_factory: Optional[NoArgAnyCallable] = None,
+) -> Any:
+    """
+    Indicates that attribute is only used internally and never mixed with regular fields.
+
+    Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant.
+
+    Private attrs are stored in model __slots__.
+
+    :param default: the attribute’s default value
+    :param default_factory: callable that will be called when a default value is needed for this attribute
+      If both `default` and `default_factory` are set, an error is raised.
+    """
+    if default is not Undefined and default_factory is not None:
+        raise ValueError('cannot specify both default and default_factory')
+
+    return ModelPrivateAttr(
+        default,
+        default_factory=default_factory,
+    )
diff --git a/pydantic/main.py b/pydantic/main.py
index bc5444a70..bc0b5be5d 100644
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -29,7 +29,7 @@ from typing import (
 from .class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators
 from .error_wrappers import ErrorWrapper, ValidationError
 from .errors import ConfigError, DictError, ExtraError, MissingError
-from .fields import SHAPE_MAPPING, ModelField, PrivateAttr, Undefined
+from .fields import SHAPE_MAPPING, ModelField, ModelPrivateAttr, PrivateAttr, Undefined
 from .json import custom_pydantic_encoder, pydantic_encoder
 from .parse import Protocol, load_file, load_str_bytes
 from .schema import default_ref_template, model_schema
@@ -215,7 +215,7 @@ class ModelMetaclass(ABCMeta):
         validators: 'ValidatorListDict' = {}
 
         pre_root_validators, post_root_validators = [], []
-        private_attributes: Dict[str, PrivateAttr] = {}
+        private_attributes: Dict[str, ModelPrivateAttr] = {}
         slots: Set[str] = namespace.get('__slots__', ())
         slots = {slots} if isinstance(slots, str) else set(slots)
 
@@ -271,7 +271,7 @@ class ModelMetaclass(ABCMeta):
 
             for var_name, value in namespace.items():
                 can_be_changed = var_name not in class_vars and not isinstance(value, untouched_types)
-                if isinstance(value, PrivateAttr):
+                if isinstance(value, ModelPrivateAttr):
                     if not is_valid_private_name(var_name):
                         raise NameError(
                             f'Private attributes "{var_name}" must not be a valid field name; '


Test patch:
diff --git a/tests/mypy/modules/success.py b/tests/mypy/modules/success.py
index 202184c93..3d48a33e1 100644
--- a/tests/mypy/modules/success.py
+++ b/tests/mypy/modules/success.py
@@ -9,7 +9,7 @@ from datetime import date, datetime
 from typing import Any, Dict, Generic, List, Optional, TypeVar
 
 from pydantic import BaseModel, NoneStr, PyObject, StrictBool, root_validator, validate_arguments, validator
-from pydantic.fields import Field
+from pydantic.fields import Field, PrivateAttr
 from pydantic.generics import GenericModel
 from pydantic.typing import ForwardRef
 
@@ -145,3 +145,7 @@ class MyConf(BaseModel):
 conf = MyConf()
 var1: date = conf.str_pyobject(2020, 12, 20)
 var2: date = conf.callable_pyobject(2111, 1, 1)
+
+
+class MyPrivateAttr(BaseModel):
+    _private_field: str = PrivateAttr()
diff --git a/tests/test_private_attributes.py b/tests/test_private_attributes.py
index 07f702c7f..962145229 100644
--- a/tests/test_private_attributes.py
+++ b/tests/test_private_attributes.py
@@ -158,5 +158,5 @@ def test_slots_are_ignored():
 
 
 def test_default_and_default_factory_used_error():
-    with pytest.raises(TypeError, match='default and default_factory args can not be used together'):
+    with pytest.raises(ValueError, match='cannot specify both default and default_factory'):
         PrivateAttr(default=123, default_factory=lambda: 321)