Repository: Dorthu/openapi3

Issue:
openapi3.errors.SpecError: Could not parse paths./api/v1/auth/login/.post.security.2, expected to be one of [['SecurityRequirement']]
Python test client:
```python
import yaml
import requests
from openapi3 import OpenAPI
url = 'http://localhost/schema_openapi/'
response = requests.get(url, allow_redirects=True)
specifications = yaml.safe_load(response.content)
print(specifications)
api = OpenAPI(specifications)
print(dir(api))
```
Schema (localhost/schema_openapi/):
```yaml
openapi: 3.0.3
info:
  title: ''
  version: 0.0.0
paths:
  /api/v1/auth/login/:
    post:
      operationId: api_v1_auth_login_create
      description: |-
        Check the credentials and return the REST Token
        if the credentials are valid and authenticated.
        Calls Django Auth login method to register User ID
        in Django session framework

        Accept the following POST parameters: username, password
        Return the REST Framework Token Object's key.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LoginRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Login'
          description: ''
  /api/v1/auth/logout/:
    get:
      operationId: api_v1_auth_logout_retrieve
      description: |-
        Calls Django logout method and delete the Token object
        assigned to the current User object.

        Accepts/Returns nothing.
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          description: No response body
    post:
      operationId: api_v1_auth_logout_create
      description: |-
        Calls Django logout method and delete the Token object
        assigned to the current User object.

        Accepts/Returns nothing.
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          description: No response body
  /api/v1/auth/password/change/:
    post:
      operationId: api_v1_auth_password_change_create
      description: |-
        Calls Django Auth SetPasswordForm save method.

        Accepts the following POST parameters: new_password1, new_password2
        Returns the success/fail message.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordChangeRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PasswordChange'
          description: ''
  /api/v1/auth/password/reset/:
    post:
      operationId: api_v1_auth_password_reset_create
      description: |-
        Calls Django Auth PasswordResetForm save method.

        Accepts the following POST parameters: email
        Returns the success/fail message.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordResetRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PasswordReset'
          description: ''
  /api/v1/auth/password/reset/confirm/:
    post:
      operationId: api_v1_auth_password_reset_confirm_create
      description: |-
        Password reset e-mail link is confirmed, therefore
        this resets the user's password.

        Accepts the following POST parameters: token, uid,
            new_password1, new_password2
        Returns the success/fail message.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PasswordResetConfirmRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PasswordResetConfirm'
          description: ''
  /api/v1/auth/user/:
    get:
      operationId: api_v1_auth_user_retrieve
      description: |-
        Reads and updates UserModel fields
        Accepts GET, PUT, PATCH methods.

        Default accepted fields: username, first_name, last_name
        Default display fields: pk, username, email, first_name, last_name
        Read-only fields: pk, email

        Returns UserModel fields.
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserDetails'
          description: ''
    put:
      operationId: api_v1_auth_user_update
      description: |-
        Reads and updates UserModel fields
        Accepts GET, PUT, PATCH methods.

        Default accepted fields: username, first_name, last_name
        Default display fields: pk, username, email, first_name, last_name
        Read-only fields: pk, email

        Returns UserModel fields.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserDetailsRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserDetails'
          description: ''
    patch:
      operationId: api_v1_auth_user_partial_update
      description: |-
        Reads and updates UserModel fields
        Accepts GET, PUT, PATCH methods.

        Default accepted fields: username, first_name, last_name
        Default display fields: pk, username, email, first_name, last_name
        Read-only fields: pk, email

        Returns UserModel fields.
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserDetailsRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserDetails'
          description: ''
  /api/v1/pages/:
    get:
      operationId: api_v1_pages_list
      description: ''
      parameters:
      - in: query
        name: content
        schema:
          type: string
      - in: query
        name: created
        schema:
          type: string
          format: date-time
      - in: query
        name: id
        schema:
          type: integer
      - in: query
        name: order
        schema:
          type: array
          items:
            type: string
            enum:
            - -id
            - -order
            - -title
            - id
            - order
            - title
        description: Ordenamiento
        explode: false
        style: form
      - in: query
        name: title
        schema:
          type: string
      - in: query
        name: updated
        schema:
          type: string
          format: date-time
      - in: query
        name: user
        schema:
          type: integer
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Page'
          description: ''
    post:
      operationId: api_v1_pages_create
      description: ''
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PageRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Page'
          description: ''
  /api/v1/pages/{id}/:
    get:
      operationId: api_v1_pages_retrieve
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this página.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Page'
          description: ''
    put:
      operationId: api_v1_pages_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this página.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PageRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Page'
          description: ''
    patch:
      operationId: api_v1_pages_partial_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this página.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PageRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Page'
          description: ''
    delete:
      operationId: api_v1_pages_destroy
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this página.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '204':
          description: No response body
  /api/v1/stocks/:
    get:
      operationId: api_v1_stocks_list
      description: ''
      parameters:
      - in: query
        name: created
        schema:
          type: string
          format: date-time
      - in: query
        name: id
        schema:
          type: integer
      - in: query
        name: name
        schema:
          type: string
      - in: query
        name: order
        schema:
          type: array
          items:
            type: string
            enum:
            - -name
            - -price
            - name
            - price
        description: Ordenamiento
        explode: false
        style: form
      - in: query
        name: price
        schema:
          type: number
          format: float
      - in: query
        name: update
        schema:
          type: string
          format: date-time
      - in: query
        name: user
        schema:
          type: integer
          nullable: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Stock'
          description: ''
    post:
      operationId: api_v1_stocks_create
      description: ''
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StockRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stock'
          description: ''
  /api/v1/stocks/{id}/:
    get:
      operationId: api_v1_stocks_retrieve
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this stock.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stock'
          description: ''
    put:
      operationId: api_v1_stocks_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this stock.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StockRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stock'
          description: ''
    patch:
      operationId: api_v1_stocks_partial_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this stock.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StockRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Stock'
          description: ''
    delete:
      operationId: api_v1_stocks_destroy
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this stock.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '204':
          description: No response body
  /api/v1/temperature/:
    get:
      operationId: api_v1_temperature_list
      description: ''
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Temperature'
          description: ''
    post:
      operationId: api_v1_temperature_create
      description: ''
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemperatureRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Temperature'
          description: ''
  /api/v1/temperature/{id}/:
    get:
      operationId: api_v1_temperature_retrieve
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this temperature.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Temperature'
          description: ''
    put:
      operationId: api_v1_temperature_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this temperature.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemperatureRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Temperature'
          description: ''
    patch:
      operationId: api_v1_temperature_partial_update
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this temperature.
        required: true
      tags:
      - api
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemperatureRequest'
        required: true
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Temperature'
          description: ''
    delete:
      operationId: api_v1_temperature_destroy
      description: ''
      parameters:
      - in: path
        name: id
        schema:
          type: integer
        description: A unique integer value identifying this temperature.
        required: true
      tags:
      - api
      security:
      - cookieAuth: []
      - tokenAuth: []
      responses:
        '204':
          description: No response body
  /schema_openapi/:
    get:
      operationId: schema_openapi_retrieve
      description: |-
        OpenApi3 schema for this API. Format can be selected via content negotiation.

        - YAML: application/vnd.oai.openapi
        - JSON: application/vnd.oai.openapi+json
      parameters:
      - in: query
        name: format
        schema:
          type: string
          enum:
          - json
          - yaml
      - in: query
        name: lang
        schema:
          type: string
          enum:
          - af
          - ar
          - ast
          - az
          - be
          - bg
          - bn
          - br
          - bs
          - ca
          - cs
          - cy
          - da
          - de
          - dsb
          - el
          - en
          - en-au
          - en-gb
          - eo
          - es
          - es-ar
          - es-co
          - es-mx
          - es-ni
          - es-ve
          - et
          - eu
          - fa
          - fi
          - fr
          - fy
          - ga
          - gd
          - gl
          - he
          - hi
          - hr
          - hsb
          - hu
          - hy
          - ia
          - id
          - io
          - is
          - it
          - ja
          - ka
          - kab
          - kk
          - km
          - kn
          - ko
          - lb
          - lt
          - lv
          - mk
          - ml
          - mn
          - mr
          - my
          - nb
          - ne
          - nl
          - nn
          - os
          - pa
          - pl
          - pt
          - pt-br
          - ro
          - ru
          - sk
          - sl
          - sq
          - sr
          - sr-latn
          - sv
          - sw
          - ta
          - te
          - th
          - tr
          - tt
          - udm
          - uk
          - ur
          - uz
          - vi
          - zh-hans
          - zh-hant
      tags:
      - schema_openapi
      security:
      - cookieAuth: []
      - tokenAuth: []
      - {}
      responses:
        '200':
          content:
            application/vnd.oai.openapi:
              schema:
                type: object
                additionalProperties: {}
            application/yaml:
              schema:
                type: object
                additionalProperties: {}
            application/vnd.oai.openapi+json:
              schema:
                type: object
                additionalProperties: {}
            application/json:
              schema:
                type: object
                additionalProperties: {}
          description: ''
components:
  schemas:
    Login:
      type: object
      properties:
        username:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
      required:
      - password
    LoginRequest:
      type: object
      properties:
        username:
          type: string
        email:
          type: string
          format: email
        password:
          type: string
      required:
      - password
    Page:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        title:
          type: string
          maxLength: 200
        content:
          type: string
        order:
          type: integer
          maximum: 32767
          minimum: -32768
        created:
          type: string
          format: date-time
          readOnly: true
        updated:
          type: string
          format: date-time
          readOnly: true
        user:
          type: integer
      required:
      - content
      - created
      - id
      - title
      - updated
      - user
    PageRequest:
      type: object
      properties:
        title:
          type: string
          maxLength: 200
        content:
          type: string
        order:
          type: integer
          maximum: 32767
          minimum: -32768
        user:
          type: integer
      required:
      - content
      - title
      - user
    PasswordChange:
      type: object
      properties:
        new_password1:
          type: string
          maxLength: 128
        new_password2:
          type: string
          maxLength: 128
      required:
      - new_password1
      - new_password2
    PasswordChangeRequest:
      type: object
      properties:
        new_password1:
          type: string
          maxLength: 128
        new_password2:
          type: string
          maxLength: 128
      required:
      - new_password1
      - new_password2
    PasswordReset:
      type: object
      description: Serializer for requesting a password reset e-mail.
      properties:
        email:
          type: string
          format: email
      required:
      - email
    PasswordResetConfirm:
      type: object
      description: Serializer for requesting a password reset e-mail.
      properties:
        new_password1:
          type: string
          maxLength: 128
        new_password2:
          type: string
          maxLength: 128
        uid:
          type: string
        token:
          type: string
      required:
      - new_password1
      - new_password2
      - token
      - uid
    PasswordResetConfirmRequest:
      type: object
      description: Serializer for requesting a password reset e-mail.
      properties:
        new_password1:
          type: string
          maxLength: 128
        new_password2:
          type: string
          maxLength: 128
        uid:
          type: string
        token:
          type: string
      required:
      - new_password1
      - new_password2
      - token
      - uid
    PasswordResetRequest:
      type: object
      description: Serializer for requesting a password reset e-mail.
      properties:
        email:
          type: string
          format: email
      required:
      - email
    Stock:
      type: object
      properties:
        id:
          type: integer
          readOnly: true
        name:
          type: string
          maxLength: 128
        price:
          type: number
          format: float
        created:
          type: string
          format: date-time
          readOnly: true
        update:
          type: string
          format: date-time
          readOnly: true
        user:
          type: integer
          nullable: true
      required:
      - created
      - id
      - name
      - price
      - update
    StockRequest:
      type: object
      properties:
        name:
          type: string
          maxLength: 128
        price:
          type: number
          format: float
        user:
          type: integer
          nullable: true
      required:
      - name
      - price
    Temperature:
      type: object
      properties:
        time:
          type: string
          format: date-time
        temperature:
          type: number
          format: float
      required:
      - temperature
    TemperatureRequest:
      type: object
      properties:
        time:
          type: string
          format: date-time
        temperature:
          type: number
          format: float
      required:
      - temperature
    UserDetails:
      type: object
      description: User model w/o password
      properties:
        pk:
          type: integer
          readOnly: true
        username:
          type: string
          description: 'Requerido. 150 carácteres como máximo. Únicamente letras,
            dígitos y @/./+/-/_ '
          pattern: ^[\w.@+-]+$
          maxLength: 150
        email:
          type: string
          format: email
          readOnly: true
        first_name:
          type: string
          maxLength: 30
        last_name:
          type: string
          maxLength: 150
      required:
      - email
      - pk
      - username
    UserDetailsRequest:
      type: object
      description: User model w/o password
      properties:
        username:
          type: string
          description: 'Requerido. 150 carácteres como máximo. Únicamente letras,
            dígitos y @/./+/-/_ '
          pattern: ^[\w.@+-]+$
          maxLength: 150
        first_name:
          type: string
          maxLength: 30
        last_name:
          type: string
          maxLength: 150
      required:
      - username
  securitySchemes:
    cookieAuth:
      type: apiKey
      in: cookie
      name: Session
    tokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: Token-based authentication with required prefix "Token"

```
Exception result:
```
Traceback (most recent call last):
  File "E:/dev/django_test/chat/client/client_drf.py", line 9, in <module>
    api = OpenAPI(specifications)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\openapi.py", line 44, in __init__
    super(OpenAPI, self).__init__([], raw_document, self)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 56, in __init__
    self._parse_data()
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\openapi.py", line 134, in _parse_data
    self.paths        = self._get('paths', ['Path'], is_map=True)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 193, in _get
    ret = Map(self.path + [field], ret, object_types, self._root)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 516, in __init__
    dct[k] = t(path + [k], v, self._root)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 56, in __init__
    self._parse_data()
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\paths.py", line 36, in _parse_data
    self.post        = self._get('post', 'Operation')
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 209, in _get
    ret = python_type(self.path + [field], ret, self._root)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 56, in __init__
    self._parse_data()
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\paths.py", line 103, in _parse_data
    self.security     = self._get('security', ['SecurityRequirement'], is_list=True)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 185, in _get
    ret = self.parse_list(ret, object_types, field)
  File "C:\ProgramData\Miniconda3\envs\django_test3\lib\site-packages\openapi3\object_base.py", line 378, in parse_list
    raise SpecError('Could not parse {}.{}, expected to be one of [{}]'.format(
openapi3.errors.SpecError: Could not parse paths./api/v1/auth/login/.post.security.2, expected to be one of [['SecurityRequirement']]
```
The schema is generated with drf-spectacular, ¿maybe is a generation problem?
Thanks.
Greats. Ricardo.


Discussion:

Patch:
diff --git a/openapi3/object_base.py b/openapi3/object_base.py
index b3e0a0d..9932822 100644
--- a/openapi3/object_base.py
+++ b/openapi3/object_base.py
@@ -217,75 +217,76 @@ class ObjectBase(object):
         self._accessed_members.append(field)
 
         ret = self.raw_element.get(field, None)
+        if ret is None:
+            return None
 
         try:
-            if ret is not None:
-                if not isinstance(object_types, list):
-                    # maybe don't accept not-lists
-                    object_types = [object_types]
-
-                if '*' in object_types and len(object_types) != 1:
-                    raise ValueError("Fields that accept any type must not specify any other types!")
-
-                # if yaml loads a value that includes a unicode character in python2,
-                # that value will come in as a ``unicode`` type instead of a ``str``.
-                # For the purposes of this library, those are the same thing, so in
-                # python2 only, we'll include ``unicode`` for any element that
-                # accepts ``str`` types.
-                if IS_PYTHON_2:
-                    if str in object_types:
-                        object_types += [unicode]
-
-
-                if is_list:
-                    if not isinstance(ret, list):
-                        raise SpecError('Expected {}.{} to be a list of [{}], got {}'.format(
-                            self.get_path, field, ','.join([str(c) for c in object_types]),
-                            type(ret)),
-                            path=self.path,
-                            element=self)
-                    ret = self.parse_list(ret, object_types, field)
-                elif is_map:
-                    if not isinstance(ret, dict):
-                        raise SpecError('Expected {}.{} to be a Map of string: [{}], got {}'.format(
-                            self.get_path, field, ','.join([str(c) for c in object_types]),
-                            type(ret)),
-                            path=self.path,
-                            element=self)
-                    ret = Map(self.path + [field], ret, object_types, self._root)
-                else:
-                    accepts_string = str in object_types
-                    found_type = False
-
-                    for t in object_types:
-                        if t == "*":
-                            found_type = True
-                            break
+            if not isinstance(object_types, list):
+                # maybe don't accept not-lists
+                object_types = [object_types]
+
+            if '*' in object_types and len(object_types) != 1:
+                raise ValueError("Fields that accept any type must not specify any other types!")
+
+            # if yaml loads a value that includes a unicode character in python2,
+            # that value will come in as a ``unicode`` type instead of a ``str``.
+            # For the purposes of this library, those are the same thing, so in
+            # python2 only, we'll include ``unicode`` for any element that
+            # accepts ``str`` types.
+            if IS_PYTHON_2:
+                if str in object_types:
+                    object_types += [unicode]
+
+
+            if is_list:
+                if not isinstance(ret, list):
+                    raise SpecError('Expected {}.{} to be a list of [{}], got {}'.format(
+                        self.get_path, field, ','.join([str(c) for c in object_types]),
+                        type(ret)),
+                        path=self.path,
+                        element=self)
+                ret = self.parse_list(ret, object_types, field)
+            elif is_map:
+                if not isinstance(ret, dict):
+                    raise SpecError('Expected {}.{} to be a Map of string: [{}], got {}'.format(
+                        self.get_path, field, ','.join([str(c) for c in object_types]),
+                        type(ret)),
+                        path=self.path,
+                        element=self)
+                ret = Map(self.path + [field], ret, object_types, self._root)
+            else:
+                accepts_string = str in object_types
+                found_type = False
+
+                for t in object_types:
+                    if t == "*":
+                        found_type = True
+                        break
 
-                        if t == str:
-                            # try to parse everything else first
-                            continue
-
-                        if isinstance(t, str):
-                            # we were given the name of a subclass of ObjectBase,
-                            # attempt to parse ret as that type
-                            python_type = ObjectBase.get_object_type(t)
-
-                            if python_type.can_parse(ret):
-                                ret = python_type(self.path + [field], ret, self._root)
-                                found_type = True
-                                break
-                        elif isinstance(ret, t):
-                            # it's already the type we need
+                    if t == str:
+                        # try to parse everything else first
+                        continue
+
+                    if isinstance(t, str):
+                        # we were given the name of a subclass of ObjectBase,
+                        # attempt to parse ret as that type
+                        python_type = ObjectBase.get_object_type(t)
+
+                        if python_type.can_parse(ret):
+                            ret = python_type(self.path + [field], ret, self._root)
                             found_type = True
                             break
+                    elif isinstance(ret, t):
+                        # it's already the type we need
+                        found_type = True
+                        break
 
-                    if not found_type:
-                        if accepts_string and isinstance(ret, str):
-                            found_type = True
+                if not found_type:
+                    if accepts_string and isinstance(ret, str):
+                        found_type = True
 
-                    if not found_type:
-                        raise_on_unknown_type(self, field, object_types, ret)
+                if not found_type:
+                    raise_on_unknown_type(self, field, object_types, ret)
         except SpecError as e:
             if self._root.validation_mode:
                 self._root.log_spec_error(e)
diff --git a/openapi3/openapi.py b/openapi3/openapi.py
index c84a8f7..81e4aba 100644
--- a/openapi3/openapi.py
+++ b/openapi3/openapi.py
@@ -64,6 +64,12 @@ class OpenAPI(ObjectBase):
 
         TODO - this should support more than just HTTP Auth
         """
+
+        # authentication is optional and can be disabled
+        if security_scheme is None:
+            self._security = None
+            return
+
         if security_scheme not in self.components.securitySchemes:
             raise ValueError('{} does not accept security scheme {}'.format(
                 self.info.title, security_scheme))
diff --git a/openapi3/paths.py b/openapi3/paths.py
index f4cae87..2c69a19 100644
--- a/openapi3/paths.py
+++ b/openapi3/paths.py
@@ -186,6 +186,9 @@ class Operation(ObjectBase):
                 # apiKey in query header data
                 self._request.headers[ss.name] = value
 
+            if ss.in_ == 'cookie':
+                self._request.cookies ={ss.name:value}
+
     def _request_handle_parameters(self, parameters={}):
         # Parameters
         path_parameters = {}
@@ -356,17 +359,22 @@ class SecurityRequirement(ObjectBase):
     def _parse_data(self):
         """
         """
-        # these only ever have one key
-        self.name  = [c for c in self.raw_element.keys()][0]
-        self.types = self._get(self.name, str, is_list=True)
+        # usually these only ever have one key
+        if len(self.raw_element.keys()) == 1:
+            self.name  = [c for c in self.raw_element.keys()][0]
+            self.types = self._get(self.name, str, is_list=True)
+        elif len(self.raw_element.keys()) == 0:
+            # optional
+            self.name = self.types = None
+
 
     @classmethod
     def can_parse(cls, dct):
         """
         This needs to ignore can_parse since the objects it's parsing are not
-        regular - they must always have only one key though.
+        regular - they must always have only one key though or be empty for Optional Security Requirements
         """
-        return len(dct.keys()) == 1 and isinstance([c for c in dct.values()][0], list)
+        return len(dct.keys()) == 1 and isinstance([c for c in dct.values()][0], list) or len(dct.keys()) == 0
 
     def __getstate__(self):
         return {self.name: self.types}
diff --git a/openapi3/schemas.py b/openapi3/schemas.py
index 7fc64bf..0fcb94b 100644
--- a/openapi3/schemas.py
+++ b/openapi3/schemas.py
@@ -116,10 +116,7 @@ class Schema(ObjectBase):
             # TODO - perhaps assert that the type of data matches the type we
             # expected
             return data
-        elif self.type == "array":
-            return [self.items.get_type()(i, self.items) for i in data]
-        else:
-            return self.get_type()(data, self)
+        return self.get_type()(data, self)
 
     def get_request_type(self):
         """


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 5f4711d..29e9003 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -136,3 +136,10 @@ def with_broken_links():
     Provides a spec with broken links defined
     """
     yield _get_parsed_yaml("with-broken-links.yaml")
+
+@pytest.fixture
+def with_securityparameters():
+    """
+    Provides a spec with security parameters
+    """
+    yield _get_parsed_yaml("with-securityparameters.yaml")
diff --git a/tests/fixtures/with-securityparameters.yaml b/tests/fixtures/with-securityparameters.yaml
new file mode 100644
index 0000000..a77befa
--- /dev/null
+++ b/tests/fixtures/with-securityparameters.yaml
@@ -0,0 +1,88 @@
+openapi: 3.0.3
+info:
+  title: ''
+  version: 0.0.0
+servers:
+  - url: http://127.0.0.1/api
+
+security:
+  - {}
+  - cookieAuth: []
+
+paths:
+  /api/v1/auth/info/:
+    get:
+      operationId: api_v1_auth_login_info
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Login'
+          description: ''
+          
+  /api/v1/auth/login/:
+    post:
+      operationId: api_v1_auth_login_create
+      description: |-
+        Check the credentials and return the REST Token
+        if the credentials are valid and authenticated.
+        Calls Django Auth login method to register User ID
+        in Django session framework
+
+        Accept the following POST parameters: username, password
+        Return the REST Framework Token Object's key.
+      tags:
+      - api
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/LoginRequest'
+        required: true
+      responses:
+        '200':
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Login'
+          description: ''
+      security:
+      - cookieAuth: []
+      - tokenAuth: []
+      - paramAuth: []
+      - basicAuth: []
+      - digestAuth: []
+      - bearerAuth: []
+      - {}
+
+components:
+  schemas:
+    Login:
+      type: string
+    LoginRequest:
+      type: string
+  securitySchemes:
+    cookieAuth:
+      type: apiKey
+      in: cookie
+      name: Session
+    tokenAuth:
+      type: apiKey
+      in: header
+      name: Authorization
+      description: Token-based authentication with required prefix "Token"
+    paramAuth:
+      type: apiKey
+      in: query
+      name: auth
+    basicAuth:
+      type: http
+      scheme: basic
+    digestAuth:
+      type: http
+      scheme: digest
+    bearerAuth:
+      type: http
+      scheme: bearer
+
diff --git a/tests/parsing_test.py b/tests/parsing_test.py
index 85a646b..3637c44 100644
--- a/tests/parsing_test.py
+++ b/tests/parsing_test.py
@@ -111,3 +111,9 @@ def test_parsing_broken_links(with_broken_links):
         "operationId and operationRef are mutually exclusive, only one of them is allowed",
         "operationId and operationRef are mutually exclusive, one of them must be specified",
     ]) == sorted(error_strs)
+
+
+def test_securityparameters(with_securityparameters):
+    spec = OpenAPI(with_securityparameters, validate=True)
+    errors = spec.errors()
+    assert len(errors) == 0
diff --git a/tests/path_test.py b/tests/path_test.py
index a320e82..26510e4 100644
--- a/tests/path_test.py
+++ b/tests/path_test.py
@@ -1,7 +1,14 @@
 """
 This file tests that paths are parsed and populated correctly
 """
+import base64
+import uuid
+
+from unittest.mock import patch, MagicMock
+from urllib.parse import urlparse
+
 import pytest
+import requests.auth
 
 from openapi3 import OpenAPI
 from openapi3.schemas import Schema
@@ -88,3 +95,62 @@ def test_operation_populated(petstore_expanded_spec):
     assert con2.schema is not None
     # again, test ref resolution elsewhere
     assert type(con2.schema) == Schema
+
+
+def test_securityparameters(with_securityparameters):
+    api = OpenAPI(with_securityparameters)
+    r = patch("requests.sessions.Session.send")
+
+    auth=str(uuid.uuid4())
+
+    # global security
+    api.authenticate('cookieAuth', auth)
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"})
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+
+
+    # path
+    api.authenticate('tokenAuth', auth)
+    resp = MagicMock(status_code=200, headers={"Content-Type": "application/json"})
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+    assert r.call_args.args[0].headers['Authorization'] == auth
+
+    api.authenticate('paramAuth', auth)
+    resp = MagicMock(status_code=200, headers={"Content-Type": "application/json"})
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+
+    parsed_url = urlparse(r.call_args.args[0].url)
+    parsed_url.query == auth
+
+    api.authenticate('cookieAuth', auth)
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"}, json=lambda: [])
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+    assert r.call_args.args[0].headers["Cookie"] == "Session=%s" % (auth,)
+
+    api.authenticate('basicAuth', (auth, auth))
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"}, json=lambda: [])
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+    r.call_args.args[0].headers["Authorization"].split(" ")[1] == base64.b64encode((auth + ':' + auth).encode()).decode()
+
+    api.authenticate('digestAuth', (auth,auth))
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"}, json=lambda: [])
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+    assert requests.auth.HTTPDigestAuth.handle_401 == r.call_args.args[0].hooks["response"][0].__func__
+
+    api.authenticate('bearerAuth', auth)
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"}, json=lambda: [])
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+    assert r.call_args.args[0].headers["Authorization"] == "Bearer %s" % (auth,)
+
+    api.authenticate(None, None)
+    resp = MagicMock(status_code=200, headers={"Content-Type":"application/json"})
+    with patch("requests.sessions.Session.send", return_value=resp) as r:
+        api.call_api_v1_auth_login_create(data={}, parameters={})
+        api.call_api_v1_auth_login_create(data={}, parameters={})