Skip to main content

Folder.name (or File.name) property doesn't pass mypy check

  • September 30, 2023
  • 5 replies
  • 201 views

  • New Participant
  • 4 replies

Hi,


let’s say that I have a folder id as string.

I can get folder object from folder id as:

folder = self._client.folder(folder_id=folder_id).get()


Now I would like to iterate over folder items that can be files or (sub)folders.


        for item in folder.get_items():

            if item.type == 'folder':

                sub_folder_path = folder_path + '/' + folder.name


For last line mypy reports:

64: error: “Folder” has no attribute “name” [attr-defined]


Looks like folder (the same is for file as well) has .name property, but it is injected somehow on the fly.


I don’t like putting #noqa nor doing hacks.

Is there anything I can do to pass mypy check with this line?


Regards,


Zoran

5 replies

rbarbosa Box
  • Developer Advocate
  • 553 replies
  • October 2, 2023

Hi @zljmob


Humm, looks like what you want is:


 for item in folder.get_items():

            if item.type == 'folder':

                sub_folder_path = folder_path + '/' + item.name


Does that solve your issue?


  • Author
  • New Participant
  • 4 replies
  • October 7, 2023

Hi rbarbosa, actually no.

Maybe I haven’t provided enought code.

Basically, self.flder_by_id(folder_id) return a folder object.

For mypy, Folder object doesnt have a name property at all.

Looks like name property is added on the fly and that is why I am getting:

error: Item "Folder" of "File | Folder" has no attribute "name" [union-attr]


If Folder class is defined with name like this:


class Folder:

    name: str

    ...


mypy wouldn’t complain.


Code with more details follows:


from boxsdk.object.file import File

from boxsdk.object.folder import Folder



folder: Folder = self.folder_by_id(folder_id)



for item in folder.get_items():

    if item.type == 'folder':

        sub_folder_path = folder_path + '/' + folder.name


For folder and item objects mypy doesn’t see name property at all.

For example folder class is defined as this:


class Folder(Item):

    """Box API endpoint for interacting with folders."""



    _item_type = 'folder'



    @api_call

    def preflight_check(self, size: int, name: str) -> Optional[str]:

        """

        Make an API call to check if a new file with given name and size can be uploaded to this folder.

        Returns an accelerator URL if one is available.



        :param size:

            The size of the file in bytes. Specify 0 for unknown file-sizes.

        :param name:

            The name of the file to be uploaded.

        :return:

            The Accelerator upload url or None if cannot get the Accelerator upload url.

        :raises:

            :class:`BoxAPIException` when preflight check fails.

        """

        return self._preflight_check(

            size=size,

            name=name,

            parent_id=self._object_id,

        )

        ...


No name here, than if you look at the Item class no name there… and so on.


Regards.


rbarbosa Box
  • Developer Advocate
  • 553 replies
  • October 10, 2023

Oh! I see what you mean @zljmob


Many times I have to go around to get Python and the Box SDK to be more type explicit.


This is typically what I do:


Consider this example:


from typing import List



from boxsdk import JWTAuth, Client



from boxsdk.object.file import File

from boxsdk.object.folder import Folder

from boxsdk.object.item import Item



def get_client() -> Client:

    config = JWTAuth.from_settings_file("config.json")

    return Client(config)



def get_folder_items(folder: Folder) -> List[Item]:

    return folder.get_items(limit=1000, offset=0)



def get_folder_by_id(client: Client, folder_id: str) -> Folder:

    return client.folder(folder_id=folder_id).get()



def get_file_by_id(client: Client, file_id: str) -> File:

    return client.file(file_id=file_id).get()



def main():

    client = get_client()

    folder = get_folder_by_id(client, "0")

    print("Current Folder: " + folder.name)

    items = get_folder_items(folder)

    for item in items:

        if isinstance(item, File):

            print("\tFile: " + item.name)

        elif isinstance(item, Folder):

            print("\tFolder: " + item.name)



if __name__ == "__main__":

    main()


The output is (I only have folders on my root):


Current Folder: All Files

        Folder: 100k

        Folder: aaaa

        Folder: Bookings

        Folder: Box UI Elements Demo

        Folder: Classification Service Demo

        Folder: JWT Folder for UI Sample Apps

        Folder: My Signed Documents

        Folder: Shared with RB

        Folder: Waivers

        Folder: Webhook


and mypy does not complain:


❯ mypy main.py

Success: no issues found in 1 source file


I know this is not ideal, and with the new generated Python SDK, the engineering folks wont make this change on the classic one.


By the way, if you have feedback on these lines for the generated SDK, now is the time to send it.


Let us know.


Cheers


  • Author
  • New Participant
  • 4 replies
  • October 10, 2023

Hi @rbarbosa,


I wander how come that you haven’t got a mypy error in lines like:


print("\tFile: " + item.name)

print("\tFolder: " + item.name)


I copied your code to my project and checked it with mypy and you are right. There are no errors at all.

How come that in your example mypy doesn’t complain but in my case it complains?


Anyway, authors of the box had the same problem. Please look at the line and notice the comment:



rbarbosa Box
  • Developer Advocate
  • 553 replies
  • October 10, 2023

Hi @zljmob



Well, we are trying to use a dynamically typed language and make it more statically typed using type hints.


So while the interpreter will only check the types at run time. The type hints are useful for tools like mypy or others like vscode extensions to check it at design time.


In the previous example notice how I created methods to get the files, folders and items and explicitly included the return types.


This way mypy and in my case a vscode extension, knows the specific type those methods are expected to return. If I don’t respect that I get a visual warning in vscode.


I’m personally a fan of type hints, and try to use them as much as possible. However Python appeals to many developers because it is dynamically typed.


I think you would like Rust, if you haven’t tried it yet. I doesn’t get more statically typed than that.


But I digress…


To your point:



  • You can probably make mypy happy with a few extra hints or a different way of specifying them, or even creating some abstraction layer like I did.

  • For this classic python SDK most likely the engineering team will not address the type hints.

  • You might want to take a look at the new generated SDK and see if the issue is still there.


Cheers


Cookie policy

We use cookies to enhance and personalize your experience. If you accept you agree to our full cookie policy. Learn more about our cookies.

 
Cookie settings