0

I have a function with optional field url

def create_ads_task(
    template_id: int,
    ad_account_id: str,
    adset_ids: List[str],
    placements: Dict[str, str],
    locales: List[Locale],
    url: Optional[HttpUrl],
) -> List[Dict[str, Union[str, int]]]:
    ...body...

When I run the tests, I get an error

E       TypeError: create_ads_task() missing 1 required positional argument: 'url'

test.py

assert create_ads_task(
    TEMPLATE_ID, AD_ACCOUNT_ID, ADSET_IDS, PLACEMENTS, LOCALES
) == [ ..... ]

How to fix test I now, but I cant understand why optional field is required. The function does not always need the url parameter, sometimes it is not passed from the frontend. How do I define the optional parameter correctly?

Jekson
  • 2,892
  • 8
  • 44
  • 79

2 Answers2

2

Optional[..] says that the parameter passed SHOULD BE of the mentioned type OR None, like in your case url should be of type HttpUrl OR None, but it does not mean that if it's not passed it will carry a default value. See the below example:

from typing import Optional

# In this b will should be passed as str or None
def test_with_optional(a: str, b: Optional[str]):
    print (a,b)
    
# In this if b is not passed, it will carry a default value of None
def test_with_none(a: str, b: str=None):
    print (a,b)
    
test_with_optional('1', None)
test_with_none('2')

test_with_optional('1') will throw error TypeError: test_with_optional() missing 1 required positional argument: 'b' because what it expects b either as a string or as None.

For your case your definition will be as below. Read more about optional parameters here

def create_ads_task(
        template_id: int,
        ad_account_id: str,
        adset_ids: List[str],
        placements: Dict[str, str],
        locales: List[Locale],
        url: Optional[HttpUrl]=None,
    ) -> List[Dict[str, Union[str, int]]]:
        ...body...
Bhagyesh Dudhediya
  • 1,800
  • 1
  • 13
  • 16
  • Yes, it works. But my question about `typing` mostly. Why Optional doesn't actually make the parameter optional – Jekson Oct 12 '21 at 09:32
  • 1
    `Optional[HttpUrl]` says that `url` SHOULD BE of type `HttpUrl` OR `None`, it does not say that if nothing is passed treat it as `None` – Bhagyesh Dudhediya Oct 12 '21 at 09:46
1

Type hinting that it is optional is not enough. You have to assign a default value to it. Like:

def create_ads_task(
    template_id: int,
    ad_account_id: str,
    adset_ids: List[str],
    placements: Dict[str, str],
    locales: List[Locale],
    url: Optional[HttpUrl] = None,
) -> List[Dict[str, Union[str, int]]]:
    ...body...
MSH
  • 1,743
  • 2
  • 14
  • 22
  • So, type `Optional` doesn't guarantee and always have to specify a default value ? – Jekson Oct 12 '21 at 09:24
  • No. In `python` to have an optional parameter you basically assign a default value to it. https://stackoverflow.com/a/67103403/2681662 Type hinting is just, well, for hinting. – MSH Oct 12 '21 at 09:27
  • What's the point of `Optianal` type if it doesn't actually make the parameter optional ?Just indicates that the parameter may not exist? – Jekson Oct 12 '21 at 09:30
  • 1
    And BTW `Optional` means the parameter has a default value. It doesn't mean it may not exist. Usually you assign `None` and parse it in the function itself if it might not exist. TL;DR an optional parameter exist but has a default value. – MSH Oct 12 '21 at 09:43
  • And https://www.w3schools.com/python/python_functions.asp we can see these parameters are called DEFAULT not optional. Maybe it's just an unfortunate naming? – MSH Oct 12 '21 at 09:46