codecov-io: # [Codecov](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=h1) Report
> Merging [#53](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=desc) into [master](https://codecov.io/gh/Azure/msrest-for-python/commit/bad8585bcbe5f92f3b2c892c8b373ee367dff70f?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/Azure/msrest-for-python/pull/53/graphs/tree.svg?token=Lny6ZO1MUF&src=pr&width=650&height=150)](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master      #53   +/-   ##
=======================================
  Coverage   77.77%   77.77%           
=======================================
  Files         260      260           
  Lines       12696    12696           
=======================================
  Hits         9874     9874           
  Misses       2822     2822
```


| [Impacted Files](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...rmat\_test\_service/operations/queries\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9VcmxNdWx0aUNvbGxlY3Rpb25Gb3JtYXQvYXV0b19yZXN0X3VybF9tdXRsaV9jb2xsZWN0aW9uX2Zvcm1hdF90ZXN0X3NlcnZpY2Uvb3BlcmF0aW9ucy9xdWVyaWVzX29wZXJhdGlvbnMucHk=) | `76.78% <0%> (ø)` | :arrow_up: |
| [...ion\_test\_service/operations/duration\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9Cb2R5RHVyYXRpb24vYXV0b19yZXN0X2R1cmF0aW9uX3Rlc3Rfc2VydmljZS9vcGVyYXRpb25zL2R1cmF0aW9uX29wZXJhdGlvbnMucHk=) | `76.62% <0%> (ø)` | :arrow_up: |
| [...ger\_bat\_byte\_service/operations/byte\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9Cb2R5Qnl0ZS9hdXRvX3Jlc3Rfc3dhZ2dlcl9iYXRfYnl0ZV9zZXJ2aWNlL29wZXJhdGlvbnMvYnl0ZV9vcGVyYXRpb25zLnB5) | `76.84% <0%> (ø)` | :arrow_up: |
| [...rvice/operations/http\_client\_failure\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9IdHRwL2F1dG9fcmVzdF9odHRwX2luZnJhc3RydWN0dXJlX3Rlc3Rfc2VydmljZS9vcGVyYXRpb25zL2h0dHBfY2xpZW50X2ZhaWx1cmVfb3BlcmF0aW9ucy5weQ==) | `71.54% <0%> (ø)` | :arrow_up: |
| [...ed\_host\_test\_client/operations/paths\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9DdXN0b21CYXNlVXJpL2F1dG9fcmVzdF9wYXJhbWV0ZXJpemVkX2hvc3RfdGVzdF9jbGllbnQvb3BlcmF0aW9ucy9wYXRoc19vcGVyYXRpb25zLnB5) | `83.33% <0%> (ø)` | :arrow_up: |
| [...nal\_test\_service/operations/implicit\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9SZXF1aXJlZE9wdGlvbmFsL2F1dG9fcmVzdF9yZXF1aXJlZF9vcHRpb25hbF90ZXN0X3NlcnZpY2Uvb3BlcmF0aW9ucy9pbXBsaWNpdF9vcGVyYXRpb25zLnB5) | `52.5% <0%> (ø)` | :arrow_up: |
| [...ionary\_service/operations/dictionary\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9Cb2R5RGljdGlvbmFyeS9hdXRvX3Jlc3Rfc3dhZ2dlcl9iYV90ZGljdGlvbmFyeV9zZXJ2aWNlL29wZXJhdGlvbnMvZGljdGlvbmFyeV9vcGVyYXRpb25zLnB5) | `74.49% <0%> (ø)` | :arrow_up: |
| [...e\_test\_service/operations/http\_retry\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9IdHRwL2F1dG9fcmVzdF9odHRwX2luZnJhc3RydWN0dXJlX3Rlc3Rfc2VydmljZS9vcGVyYXRpb25zL2h0dHBfcmV0cnlfb3BlcmF0aW9ucy5weQ==) | `72.46% <0%> (ø)` | :arrow_up: |
| [...t\_service/operations/datetimerfc1123\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9Cb2R5RGF0ZVRpbWVSZmMxMTIzL2F1dG9fcmVzdF9yZmMxMTIzX2RhdGVfdGltZV90ZXN0X3NlcnZpY2Uvb3BlcmF0aW9ucy9kYXRldGltZXJmYzExMjNfb3BlcmF0aW9ucy5weQ==) | `74.39% <0%> (ø)` | :arrow_up: |
| [...test\_service/operations/polymorphism\_operations.py](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree#diff-dGVzdC92YW5pbGxhL0V4cGVjdGVkL0FjY2VwdGFuY2VUZXN0cy9Cb2R5Q29tcGxleC9hdXRvX3Jlc3RfY29tcGxleF90ZXN0X3NlcnZpY2Uvb3BlcmF0aW9ucy9wb2x5bW9ycGhpc21fb3BlcmF0aW9ucy5weQ==) | `71.42% <0%> (ø)` | :arrow_up: |
| ... and [45 more](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Azure/msrest-for-python/pull/53?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/Azure/msrest-for-python/pull/53?src=pr&el=footer). Last update [bad8585...42a6a8b](https://codecov.io/gh/Azure/msrest-for-python/pull/53?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

diff --git a/msrest/configuration.py b/msrest/configuration.py
index 7b47b39..4b46b56 100644
--- a/msrest/configuration.py
+++ b/msrest/configuration.py
@@ -44,6 +44,19 @@ from .pipeline import (
 from .version import msrest_version
 
 
+def default_session_configuration_callback(session, global_config, local_config, **kwargs):
+    """Configuration callback if you need to change default session configuration.
+
+    :param requests.Session session: The session.
+    :param Configuration global_config: The global configuration.
+    :param dict local_config: The on-the-fly configuration passed on the call.
+    :param dict kwargs: The current computed values for session.request method.
+    :return: Must return kwargs, to be passed to session.request. If None is return, initial kwargs will be used.
+    :rtype: dict
+    """
+    return kwargs
+
+
 class Configuration(object):
     """Client configuration.
 
@@ -79,6 +92,8 @@ class Configuration(object):
         # - kwargs['msrest']['session'] with the current session
         self.hooks = []
 
+        self.session_configuration_callback = default_session_configuration_callback
+
         self._config = configparser.ConfigParser()
         self._config.optionxform = str
 
diff --git a/msrest/serialization.py b/msrest/serialization.py
index 1902ca2..cc03063 100644
--- a/msrest/serialization.py
+++ b/msrest/serialization.py
@@ -350,6 +350,7 @@ class Serializer(object):
             }
         self.dependencies = dict(classes) if classes else {}
         self.key_transformer = full_restapi_key_transformer
+        self.client_side_validation = True
 
     def _serialize(self, target_obj, data_type=None, **kwargs):
         """Serialize data into a string according to type.
@@ -440,9 +441,10 @@ class Serializer(object):
                 raise_with_traceback(
                     SerializationError, "Unable to build a model: "+str(err), err)
 
-        errors = _recursive_validate(data_type, data)
-        if errors:
-            raise errors[0]
+        if self.client_side_validation:
+            errors = _recursive_validate(data_type, data)
+            if errors:
+                raise errors[0]
         return self._serialize(data, data_type, **kwargs)
 
     def url(self, name, data, data_type, **kwargs):
@@ -454,7 +456,8 @@ class Serializer(object):
         :raises: TypeError if serialization fails.
         :raises: ValueError if data is None
         """
-        data = self.validate(data, name, required=True, **kwargs)
+        if self.client_side_validation:
+            data = self.validate(data, name, required=True, **kwargs)
         try:
             output = self.serialize_data(data, data_type, **kwargs)
             if data_type == 'bool':
@@ -478,7 +481,8 @@ class Serializer(object):
         :raises: TypeError if serialization fails.
         :raises: ValueError if data is None
         """
-        data = self.validate(data, name, required=True, **kwargs)
+        if self.client_side_validation:
+            data = self.validate(data, name, required=True, **kwargs)
         try:
             if data_type in ['[str]']:
                 data = ["" if d is None else d for d in data]
@@ -504,7 +508,8 @@ class Serializer(object):
         :raises: TypeError if serialization fails.
         :raises: ValueError if data is None
         """
-        data = self.validate(data, name, required=True, **kwargs)
+        if self.client_side_validation:
+            data = self.validate(data, name, required=True, **kwargs)
         try:
             if data_type in ['[str]']:
                 data = ["" if d is None else d for d in data]
diff --git a/msrest/service_client.py b/msrest/service_client.py
index d0e6fdb..eed50c5 100644
--- a/msrest/service_client.py
+++ b/msrest/service_client.py
@@ -145,6 +145,11 @@ class ServiceClient(object):
         for protocol in self._protocols:
             session.mount(protocol,
                           requests.adapters.HTTPAdapter(max_retries=max_retries))
+
+        output_kwargs = self.config.session_configuration_callback(session, self.config, config, **kwargs)
+        if output_kwargs is not None:
+            kwargs = output_kwargs
+
         return kwargs
 
     def send_formdata(self, request, headers=None, content=None, **config):

Add support to disable validation on the runtime side
Should be a flag in configuration to disable runtime validation.

This flag should be default to `client-side-validation` value from Autorest:
https://github.com/Azure/autorest/issues/1583#issuecomment-311142690

FYI @matthchr 
diff --git a/tests/test_client.py b/tests/test_client.py
index 8e73444..ee10d48 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -50,6 +50,22 @@ class TestServiceClient(unittest.TestCase):
         self.creds = mock.create_autospec(OAuthTokenAuthentication)
         return super(TestServiceClient, self).setUp()
 
+    def test_session_callback(self):
+
+        client = ServiceClient(self.creds, self.cfg)
+        local_session = requests.Session()
+
+        def callback(session, global_config, local_config, **kwargs):
+            self.assertIs(session, local_session)
+            self.assertIs(global_config, self.cfg)
+            self.assertTrue(local_config["test"])
+            return {'used_callback': True}
+
+        self.cfg.session_configuration_callback = callback
+
+        output_kwargs = client._configure_session(local_session, **{"test": True})
+        self.assertTrue(output_kwargs['used_callback'])
+
     def test_client_request(self):
 
         client = ServiceClient(self.creds, self.cfg)
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index c6cf07d..4d6a80a 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -172,6 +172,32 @@ class TestRuntimeSerialized(unittest.TestCase):
         self.s = Serializer({'TestObj': self.TestObj})
         return super(TestRuntimeSerialized, self).setUp()
 
+    def test_validation_flag(self):
+        s = Serializer()
+        s.client_side_validation = True
+
+        with self.assertRaises(ValidationError):
+            s.query("filter", "", "str", min_length=666)
+        with self.assertRaises(ValidationError):
+            s.url("filter", "", "str", min_length=666)
+        with self.assertRaises(ValidationError):
+            s.header("filter", "", "str", min_length=666)
+
+        test_obj = self.TestObj()
+        self.TestObj._validation = {
+            'attr_b': {'required': True},
+        }
+        test_obj.attr_b = None
+
+        with self.assertRaises(ValidationError):
+            self.s.body(test_obj, 'TestObj')
+
+        s.client_side_validation = False
+        s.query("filter", "", "str", min_length=666)
+        s.url("filter", "", "str", min_length=666)
+        s.header("filter", "", "str", min_length=666)
+        s.body(test_obj, 'TestObj')
+
     def test_serialize_direct_model(self):
         testobj = self.TestObj()
         testobj.attr_a = "myid"

pip install -e .[dev]
parse_log_pytest
pytest
pytest
3.6
dev_requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
charset-normalizer==2.0.12
idna==3.10
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isodate==0.6.1
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
-e git+https://github.com/Azure/msrest-for-python.git@bad8585bcbe5f92f3b2c892c8b373ee367dff70f#egg=msrest
oauthlib==3.2.2
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
requests==2.27.1
requests-oauthlib==2.0.0
six==1.17.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: msrest-for-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==2.0.12
      - idna==3.10
      - isodate==0.6.1
      - oauthlib==3.2.2
      - requests==2.27.1
      - requests-oauthlib==2.0.0
      - six==1.17.0
      - urllib3==1.26.20
prefix: /opt/conda/envs/msrest-for-python

tests/test_client.py::TestServiceClient::test_session_callback tests/test_serialization.py::TestRuntimeSerialized::test_validation_flag
tests/test_client.py::TestServiceClient::test_client_formdata_send tests/test_client.py::TestServiceClient::test_client_header tests/test_client.py::TestServiceClient::test_client_request tests/test_client.py::TestServiceClient::test_client_send tests/test_client.py::TestServiceClient::test_format_data tests/test_client.py::TestServiceClient::test_format_url tests/test_serialization.py::TestModelDeserialization::test_response tests/test_serialization.py::TestRuntimeSerialized::test_attr_bool tests/test_serialization.py::TestRuntimeSerialized::test_attr_dict_simple tests/test_serialization.py::TestRuntimeSerialized::test_attr_duration tests/test_serialization.py::TestRuntimeSerialized::test_attr_enum tests/test_serialization.py::TestRuntimeSerialized::test_attr_int tests/test_serialization.py::TestRuntimeSerialized::test_attr_list_complex tests/test_serialization.py::TestRuntimeSerialized::test_attr_list_simple tests/test_serialization.py::TestRuntimeSerialized::test_attr_none tests/test_serialization.py::TestRuntimeSerialized::test_attr_sequence tests/test_serialization.py::TestRuntimeSerialized::test_attr_str tests/test_serialization.py::TestRuntimeSerialized::test_empty_list tests/test_serialization.py::TestRuntimeSerialized::test_key_type tests/test_serialization.py::TestRuntimeSerialized::test_model_validate tests/test_serialization.py::TestRuntimeSerialized::test_obj_serialize_none tests/test_serialization.py::TestRuntimeSerialized::test_obj_with_malformed_map tests/test_serialization.py::TestRuntimeSerialized::test_obj_with_mismatched_map tests/test_serialization.py::TestRuntimeSerialized::test_obj_without_attr_map tests/test_serialization.py::TestRuntimeSerialized::test_polymorphic_serialization tests/test_serialization.py::TestRuntimeSerialized::test_serialize_datetime tests/test_serialization.py::TestRuntimeSerialized::test_serialize_direct_model tests/test_serialization.py::TestRuntimeSerialized::test_serialize_empty_iter tests/test_serialization.py::TestRuntimeSerialized::test_serialize_json_obj tests/test_serialization.py::TestRuntimeSerialized::test_serialize_object tests/test_serialization.py::TestRuntimeSerialized::test_serialize_primitive_types tests/test_serialization.py::TestRuntimeSerialized::test_validate tests/test_serialization.py::TestRuntimeDeserialized::test_attr_bool tests/test_serialization.py::TestRuntimeDeserialized::test_attr_int tests/test_serialization.py::TestRuntimeDeserialized::test_attr_list_complex tests/test_serialization.py::TestRuntimeDeserialized::test_attr_list_in_list tests/test_serialization.py::TestRuntimeDeserialized::test_attr_list_simple tests/test_serialization.py::TestRuntimeDeserialized::test_attr_none tests/test_serialization.py::TestRuntimeDeserialized::test_attr_str tests/test_serialization.py::TestRuntimeDeserialized::test_basic_deserialization tests/test_serialization.py::TestRuntimeDeserialized::test_cls_method_deserialization tests/test_serialization.py::TestRuntimeDeserialized::test_deserialize_datetime tests/test_serialization.py::TestRuntimeDeserialized::test_deserialize_object tests/test_serialization.py::TestRuntimeDeserialized::test_deserialize_storage tests/test_serialization.py::TestRuntimeDeserialized::test_invalid_json tests/test_serialization.py::TestRuntimeDeserialized::test_non_obj_deserialization tests/test_serialization.py::TestRuntimeDeserialized::test_obj_with_malformed_map tests/test_serialization.py::TestRuntimeDeserialized::test_obj_with_no_attr tests/test_serialization.py::TestRuntimeDeserialized::test_personalize_deserialization tests/test_serialization.py::TestRuntimeDeserialized::test_polymorphic_deserialization tests/test_serialization.py::TestRuntimeDeserialized::test_polymorphic_deserialization_with_escape tests/test_serialization.py::TestRuntimeDeserialized::test_robust_deserialization tests/test_serialization.py::TestModelInstanceEquality::test_model_instance_equality
msrest/service_client.py msrest/configuration.py msrest/serialization.py
msrest/service_client.py msrest/configuration.py msrest/serialization.py