Giter Site home page Giter Site logo

ramnes / notion-sdk-py Goto Github PK

View Code? Open in Web Editor NEW
1.6K 25.0 127.0 3.58 MB

The official Notion API client library, but rewritten in Python! (sync + async)

Home Page: https://ramnes.github.io/notion-sdk-py

License: MIT License

Python 100.00%
notion notion-api async python httpx dataclasses python-client api-client

notion-sdk-py's Introduction

notion-sdk-py

PyPI Supported Python Versions
License Code style Coverage Package health
Code Quality Tests Docs

notion-sdk-py is a simple and easy to use client library for the official Notion API.

It is meant to be a Python version of the reference JavaScript SDK, so usage should be very similar between both. 😊 (If not, please open an issue or PR!)

πŸ“’ Announcement (28-12-2023) β€” Release 2.2.1 is out and fixes iteration helpers.

What's new in 2.2.0 (26-12-2023):

  • Icons and covers can now be removed from pages.
  • filter_properties has been added to notion.pages.retrieve.
  • You can now pass your own start_cursor in the iteration helpers.

Installation

pip install notion-client

Usage

Use Notion's Getting Started Guide to get set up to use Notion's API.

Import and initialize a client using an integration token or an OAuth access token.

import os
from notion_client import Client

notion = Client(auth=os.environ["NOTION_TOKEN"])

In an asyncio environment, use the asynchronous client instead:

from notion_client import AsyncClient

notion = AsyncClient(auth=os.environ["NOTION_TOKEN"])

Make a request to any Notion API endpoint.

See the complete list of endpoints in the API reference.

from pprint import pprint

list_users_response = notion.users.list()
pprint(list_users_response)

or with the asynchronous client:

list_users_response = await notion.users.list()
pprint(list_users_response)

This would output something like:

{'results': [{'avatar_url': 'https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg',
              'id': 'd40e767c-d7af-4b18-a86d-55c61f1e39a4',
              'name': 'Avocado Lovelace',
              'object': 'user',
              'person': {'email': '[email protected]'},
              'type': 'person'},
             ...]}

All API endpoints are available in both the synchronous and asynchronous clients.

Endpoint parameters are grouped into a single object. You don't need to remember which parameters go in the path, query, or body.

my_page = notion.databases.query(
    **{
        "database_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af",
        "filter": {
            "property": "Landmark",
            "rich_text": {
                "contains": "Bridge",
            },
        },
    }
)

Handling errors

If the API returns an unsuccessful response, an APIResponseError will be raised.

The error contains properties from the response, and the most helpful is code. You can compare code to the values in the APIErrorCode object to avoid misspelling error codes.

import logging
from notion_client import APIErrorCode, APIResponseError

try:
    my_page = notion.databases.query(
        **{
            "database_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af",
            "filter": {
                "property": "Landmark",
                "rich_text": {
                    "contains": "Bridge",
                },
            },
        }
    )
except APIResponseError as error:
    if error.code == APIErrorCode.ObjectNotFound:
        ...  # For example: handle by asking the user to select a different database
    else:
        # Other error handling code
        logging.error(error)

Logging

The client emits useful information to a logger. By default, it only emits warnings and errors.

If you're debugging an application, and would like the client to log request & response bodies, set the log_level option to logging.DEBUG.

notion = Client(
    auth=os.environ["NOTION_TOKEN"],
    log_level=logging.DEBUG,
)

You may also set a custom logger to emit logs to a destination other than stdout. Have a look at Python's logging cookbook if you want to create your own logger.

Client options

Client and AsyncClient both support the following options on initialization. These options are all keys in the single constructor parameter.

Option Default value Type Description
auth None string Bearer token for authentication. If left undefined, the auth parameter should be set on each request.
log_level logging.WARNING int Verbosity of logs the instance will produce. By default, logs are written to stdout.
timeout_ms 60_000 int Number of milliseconds to wait before emitting a RequestTimeoutError
base_url "https://api.notion.com" string The root URL for sending API requests. This can be changed to test with a mock server.
logger Log to console logging.Logger A custom logger.

Full API responses

The following functions can distinguish between full and partial API responses.

Function Purpose
is_full_page Determine whether an object is a full Page object
is_full_block Determine whether an object is a full Block object
is_full_database Determine whether an object is a full Database object
is_full_page_or_database Determine whether an object is a full Page object or Database object
is_full_user Determine whether an object is a full User object
is_full_comment Determine whether an object is a full Comment object
from notion_client.helpers import is_full_page

full_or_partial_pages = await notion.databases.query(
    database_id="897e5a76-ae52-4b48-9fdf-e71f5945d1af"
)

for page in full_or_partial_pages["results"]:
    if not is_full_page_or_database(page):
        continue
    print(f"Created at: {page['created_time']}")

Utility functions

These functions can be helpful for dealing with any of the paginated APIs.

iterate_paginated_api(function, **kwargs) and its async version async_iterate_paginated_api(function, **kwargs) turn any paginated API into a generator.

The function parameter must accept a start_cursor argument. Example: notion.blocks.children.list.

from notion_client.helpers import iterate_paginated_api

for block in iterate_paginated_api(
    notion.databases.query, database_id="897e5a76-ae52-4b48-9fdf-e71f5945d1af"
):
    # Do something with block.
    ...

If you don't need a generator, collect_paginated_api(function, **kwargs) and its async version async_collect_paginated_api(function, **kwargs) have the same behavior than the previous functions, but return a list of all results from the paginated API.

from notion_client.helpers import collect_paginated_api

all_results = collect_paginated_api(
    notion.databases.query, database_id="897e5a76-ae52-4b48-9fdf-e71f5945d1af"
)

Testing

Run the tests with the pytest command. If you want to test against all Python versions, you can run tox instead.

The tests are using pytest-vcr's cassettes for simulating requests to the Notion API. To create new tests or run them without cassettes, you need to set up the environment variables NOTION_TOKEN and NOTION_TEST_PAGE_ID (a page where your integration has all the capabilities enabled).

The code will use the page at NOTION_TEST_PAGE_ID to generate a temporary environment with the Notion objects to be tested, which will be deleted at the end of the session.

Requirements

This package supports the following minimum versions:

  • Python >= 3.7
  • httpx >= 0.15.0

Earlier versions may still work, but we encourage people building new applications to upgrade to the current stable.

Getting help

If you want to submit a feature request for Notion's API, or are experiencing any issues with the API platform, please email [email protected].

If you found a bug with the library, please submit an issue.

notion-sdk-py's People

Contributors

aahnik avatar al1p avatar alecthegeek avatar andrewdircks avatar baydinvladislav avatar blue-hope avatar cbrenton avatar cclauss avatar dr0p42 avatar echo724 avatar estheruary avatar florianwilhelm avatar jheddings avatar kierun avatar mixeden avatar mutt0-ds avatar nicobako avatar nlecoy avatar ramnes avatar sgskt avatar smarttommyau avatar sourcery-ai-bot avatar ssredpanda avatar sudosubin avatar thompsonmina avatar timo-codecentric avatar tjhoff avatar yangxinyi-bigdata avatar yunlong-he avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notion-sdk-py's Issues

in query empty start_cursor option throws error

hi,

I try to make a query:

next_cursor = None
results = notion.databases.query(
        **{
            "database_id": DB_GMB_ID,
            "start_cursor": next_cursor
        }
    )

I receive the error:

APIResponseError: body failed validation: body.start_cursor should be a string or `undefined`, instead was `null`.

"" produces the same result.

What value should be used to satisfy undefined start_cursor?

Client-side validation

I am now using this sdk to do some develop. But usually i get the APIResponseError error.

APIResponseError: body failed validation.

I plan to write a simple validator in my code, and I'm curious if this feature will be added in the future.

Question: the struggle is real

Hi Guys,

this is more of a question i have. I got started and tried the one example to display the users.
That worked fine. However, after that i tried to find a notion page by it's id.
So my first question, where to find that page_id ? I thought about it's the hash value on the link
of the notion page (however, in the example it includes - and in the hash not.

After that, i just wanted to list all pages that are available, how to do that?
I could not find anything like notion.pages.list() .

Last but not least, and more of a general understanding question what are blocks?
How to see what blocks are saved?

Sorry if this questions are to simple for you, peeps.

Cheers
Chris

Object of type Decimal is not JSON serializable notion api

Hi πŸ‘‹,
I get this error TypeError: Object of type Decimal is not JSON serializable notion api while trying to create a page with a "number" property type and a decimal number as the input.

I think the error is in "_content.py" line 175.

def encode_json(json: Any) -> Tuple[Dict[str, str], ByteStream]:
    body = json_dumps(json).encode("utf-8")
    content_length = str(len(body))
    content_type = "application/json"
    headers = {"Content-Length": content_length, "Content-Type": content_type}
    return headers, ByteStream(body)`

I don't know how to solve this problem.
Thank you. 😁

Update method of PagesEndpoint class never handles archived pages

Calling something like
archivedpage = notion.pages.update(page_id, **{"archived":True, "properties":properties})
will never result in the page being archived. Tested calling the same route on curl and it works

Did some digging and i think i figured out where the bug is from,

def update(self, page_id: str, **kwargs: Any) -> SyncAsync[Any]:
        return self.parent.request(
            path=f"pages/{page_id}",
            method="PATCH",
            body=pick(kwargs, "properties"),
            auth=kwargs.get("auth"),
        )

The line body=pick(kwargs, "properties") in the update method doesn't pick up the archived keyword from kwargs, resulting in only the properties item being added to body

notion_client.errors.APIResponseError: Value is expected to be rich_text. Link is expected to be files.

Issue: when I try to add data to a database, I get errors because of the properties that are expected are incorrectly setup in the database, and the error returned doesn't helps.

The Following an example from: https://github.com/ramnes/notion-sdk-py/wiki/Examples#crud

I have had incorrectly created/setup the following properties in my db:

(Property Name: Type)
Name: Text
Tags: Multi-Select
Value: Text
Link: Files

Therefore the APIResponseError returned:
Value is expected to be rich_text. Link is expected to be files.
but this is wrong, since what's the error is saying is that the type that Value is supposed to be expecting is "rich_text", but it's actually telling me the actual property type... instead it should have returned something like this:

notion_client.errors.APIResponseError: Value is expected to be Number. Link is expected to be URL.

This took me a while to understand, because I hadn't look at the schema that it's already setup:

new_page_props = {
        'Name': {'title': [{'text': {'content': f"My Page of {rand_page_type} {page_id}"}}]},
        'Value': {'number': page_id},
        'Link': {'type': 'url', 'url': f"http://examples.org/page/{page_id}"},
        'Tags': {'type': 'multi_select', 'multi_select': [{'name': rand_page_type}]}
    }

Stack Trace:

Traceback (most recent call last):
  File "C:\Users\user\Documents\project\bots\test_notion_api.py", line 44, in <module>
    notion_page = notion.pages.create(
  File "C:\Users\maris\miniconda3\envs\an_env\lib\site-packages\notion_client\api_endpoints.py", line 197, in create
    return self.parent.request(
  File "C:\Users\maris\miniconda3\envs\an_env\lib\site-packages\notion_client\client.py", line 192, in request
    return self._parse_response(response)
  File "C:\Users\maris\miniconda3\envs\an_env\lib\site-packages\notion_client\client.py", line 124, in _parse_response
    raise APIResponseError(response, body["message"], code)
notion_client.errors.APIResponseError: Value is expected to be rich_text. Link is expected to be files.

Process finished with exit code 1

Solution: Be able to get a proper error message with the actual type that's expected by the read values, and not the ones that are setup in the database.

Search inside Client

Is there a reason the client.search doesn't use the Client?

The async client could also implements the search in async Fashion

httpx.HTTPStatusError: Client error '400 Bad Request' for url 'https://api.notion.com/v1/databases/897e5a76-ae52-4b48-9fdf-e71f5945d1af/query' when running example

This minimal example from the documentation fails for me:

import os
from notion_client import Client

notion = Client(auth=os.environ["NOTION_TOKEN"])
my_page = notion.databases.query(
    **{
        "database_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af",
        "filter": {
            "property": "Landmark",
            "text": {
                "contains": "Bridge",
            },
        },
    }
)

Other lookups work, it's specifically this filtering down to get an item using the databases.query endpoint. Do you know what might be happening?

Feature Request: log full request data

When debugging, it would be helpful to view the entire payload being sent to / from the Notion API. That's a lot of information to consume for DEBUG, but perhaps there would be a "special" named logger that could be configured to dump the request and response data?

Implement all endpoints

  • blocks.children.append()
  • blocks.children.list()
  • databases.list()
  • databases.query()
  • databases.retrieve()
  • pages.create()
  • pages.retrieve()
  • pages.update()
  • search()
  • users.list()
  • users.retrieve()

ORM for notion databases

Django ORM, sqlalchemy etc, are examples where you write python classes and call python functions, but under the hood SQL queries get carried out.

It would be great to have an ORM, that under the hood would carry out various interactions using Notion API. For the end user, it will be a high level experience.

Example usage

# a vague example usage

class Student(NotionBaseModel):
    name: Title
    address: Text
    last_edited: LastEditedTime

student = Student("Aahnik","India") # will add a row to the table

Data structures

Hi Ramnes,

I love the work you've done so far on the Notion SDK for python. I had a couple ideas to implement in the SDK if you thought they'd be of value:

  • Simple data classes on top of the various API objects for Typing and abstraction.
  • Implementing natural pythonic pagination and iteration

I'll fork the repo and start work on these. Let me know what you think of these and I can make a PR when I'm done.

Cheers,
Jeadie

Adding A Formatting Option

I would like to add a formatter that will automatically create the dictionaries in their correct format based on the property. I think this will help make the code just more readable since without it there are just extremely large dictionaries and a whole bunch of curly braces. I personally use this formatter for my own scripts that I use to automate some things on Notion.

For example:

Before using a formatter:

create_details = {
    "parent": DB_ID,
    "properties": {
        "some date property": {
            "date": {
                "start": "starting date",
                "end": "ending date",
            }
        },
        "some multi select property": {
            "multi_select": [
                {
                    "name": "option name"
                },
                {
                    "name": "option name",
                    "color": "option color"
                }
            ]
        },
        "some select property": {
            "select": {
                "option": "option name",
                "color": "option color"
            }
        }
    }
}

After using a formatter:

# this is needed because `formatter.multi_select` takes a dictionary
# as the input
multi_select_options = {
    "option one": "default",
    "option two": "color"
}
# the same dictionary, but using the formatter
create_details = {
    "parent": DB_ID,
    "properties": {
        "some date property": formatter.date("start date", "end date"),
        "some multi select property": formatter.multi_select(multi_select_options),
        "some select property": formatter.select("option name", "option color")
    }
}

Add documentation to source with examples

Although it is possible to translate the examples on the Notion API page or the examples on the JS SDK to methods and attributes on this SDK, it would be nice to reduce this "mental work" by adding documentation and examples to python source code.

Adding one example for each action bellow is a good start point:

  • listing databases
  • listing pages
  • searching databases
  • searching pages
  • creating pages
  • updating pages

Missing examples of creating databases

Hello,
I'm having problems creating a database, mostly due to lack of any examples.
I can create a page just fine, but am having problems with databases.
I think I just get type/format of the title and properties wrong.

Would be great to have an example of how to create a database with a title and a text property.

Thanks for your great work!

Search does not return root page, when root is not a workpace parent level page

This is weird. I think this is a problem with Notion API and not this library. But opening this issue, so that it can be closed when this is solved.

Problem

Suppose I create a page "my page name", and add my integration to this page, then

notion.search(query="my page name")

will not give this page.

If I query for any subpage/database in this page/page within a database on this page, I will get it successfully.

Steps to reproduce

Create a new integration named "Issue32Inti".

Duplicate this page https://www.notion.so/aahnik/notion-sdk-py-540f8e2b79914654ba103c5d8a03e10e and [update]* put it inside a parent level page .share this with your integration.

Note: the page should be a subpage. Not a parent-level page. ie, it should be inside a parent page.

Now, Initialize your client object notion.

Root page

obj = notion.search(query="notion-sdk-py") # root page
Result ❌
   { 'object': 'list',
      'results': [],
      'next_cursor': None,
      'has_more': False,
    } 

Database

obj = notion.search(query="People") # database
Result βœ…
{ 'object': 'list',
 'results': [{'object': 'database',
   'id': '99572135-4646-49bd-95a1-4ff08f79c7a5',
   'created_time': '2021-05-19T04:50:21.634Z',
   'last_edited_time': '2021-05-19T04:57:00.000Z',
   'title': [{'type': 'text',
     'text': {'content': 'People', 'link': None},
     'annotations': {'bold': False,
      'italic': False,
      'strikethrough': False,
      'underline': False,
      'code': False,
      'color': 'default'},
     'plain_text': 'People',
     'href': None}],
   'properties': {'Tags': {'id': 'EcDa',
     'type': 'multi_select',
     'multi_select': {'options': [{'id': '2cd67f95-2f58-4223-bc0a-de03bf87c1e2',
        'name': 'python',
        'color': 'default'},
       {'id': 'f2ae0eef-5611-4da8-97e0-9208ecec44c1',
        'name': 'expert',
        'color': 'blue'},
       {'id': '573d40b5-42bd-4035-815d-73fc4aa7d208',
        'name': 'learner',
        'color': 'purple'}]}},
    'Website': {'id': 'Z{nX', 'type': 'url', 'url': {}},
    'GitHub': {'id': '`<Mi', 'type': 'rich_text', 'rich_text': {}},
    'Name': {'id': 'title', 'type': 'title', 'title': {}}}}],
 'next_cursor': None,
 'has_more': False}

Subpage

obj = notion.search(query="Subpage 1") # subpage
Result βœ…
{ 'object': 'list',
 'results': [{'object': 'page',
   'id': '4e8705d7-e6a5-4b23-853f-3b2fcc9f18fe',
   'created_time': '2021-05-19T04:45:47.349Z',
   'last_edited_time': '2021-05-19T04:49:00.000Z',
   'parent': {'type': 'page_id',
    'page_id': '540f8e2b-7991-4654-ba10-3c5d8a03e10e'},
   'archived': False,
   'properties': {'title': {'id': 'title',
     'type': 'title',
     'title': [{'type': 'text',
       'text': {'content': 'Subpage 1', 'link': None},
       'annotations': {'bold': False,
        'italic': False,
        'strikethrough': False,
        'underline': False,
        'code': False,
        'color': 'default'},
       'plain_text': 'Subpage 1',
       'href': None}]}}}],
 'next_cursor': None,
 'has_more': False}

[update] means that portion was later edited.

notion_client.errors.APIResponseError: This API is deprecated when trying to fetch databases

When trying to list databases I get notion_client.errors.APIResponseError: This API is deprecated when trying to fetch databases exception:

import os
from notion_client import Client

notion = Client(auth=os.getenv("NOTION_SECRET"))
databases = notion.databases.list()  # raises notion_client.errors.APIResponseError exception

pytest log:

self = <notion_client.client.Client object at 0x10a38ce80>, response = <Response [400 Bad Request]>

    def _parse_response(self, response: Response) -> Any:
        try:
            response.raise_for_status()
        except httpx.HTTPStatusError as error:
            try:
                body = error.response.json()
                code = body.get("code")
            except json.JSONDecodeError:
                code = None
            if code and is_api_error_code(code):
>               raise APIResponseError(response, body["message"], code)
E               notion_client.errors.APIResponseError: This API is deprecated.
  • Python: 3.9.9
  • notion-client version: 1.0.0

Library was installed using poetry with:

$ poetry add notion-client

Leverage type hints

  • Add complete type hints for all signatures
  • Bundle a py.typed file
  • Make Mypy run in the CI

πŸ› Boolean always evaluated to `True`

I'm trying to generate text with styling.

For example, if I want bold + italic text on a blue background, I do the following :

from notion_client import Client

text = {
    "object": "block",
    "type": "paragraph",
    "paragraph": {
        "text": [
            {
                "type": "text",
                "text": {"content": "Test"},
                "annotations": {
                    "bold": True,
                    "italic": True,
                    "strikethrough": False,
                    "underline": False,
                    "code": False,
                    "color": "blue_background",
                },
            }
        ]
    },
}

notion = Client(auth=AUTH_TOKEN)
notion.blocks.children.append(block_id=BLOCK_ID, children=[text])

Expected result :
image

Actual result :
image

Every boolean annotations are set to True, I don't know why...


It works properly if simply remove the annotations I want to set to False :

text = {
    "object": "block",
    "type": "paragraph",
    "paragraph": {
        "text": [
            {
                "type": "text",
                "text": {"content": "Test"},
                "annotations": {
                    "bold": True,     # Note : I can set this to False, the result is the same
                    "italic": True,     # Note : I can set this to False, the result is the same
                    "color": "blue_background",
                },
            }
        ]
    },
}

Defining Data Transfer Objects (in terms of `@dataclass`) for easier development

Hello, thank you for creating such a great project.

I was wondering if there is a plan to add pre-defiend DTOs for API request / response since the official API is out of beta.

Currently, API endpoint invocations return generic SyncAsync[T] types, which makes development a bit cumbersome.

From what I am aware of, the following libs are options for defining DTOs:

  • dataclasses is built-in from 3.7, but cannot serialize / deserialize nested object (dataclasses-json).
  • pydantic has good serialization / deserialization features, but a bit heavier than the other.

Upload to database w/ external source

Hello,
I am struggling to upload to database from an external source, is there an issue with the object that I send? (I tried w/ and w/out [])

from notion_client import Client


token = ""
database_id = ""
url = ""

client = Client(auth=token)
client.pages.create(
    parent={'database_id': database_id},
    properties={
        'Title': [{'text': {'content': 'Testing upload'}}],
        "File": [{
            "files": [
                {
                    "type": "external",
                    "name": "pdf",
                    "external": url
                }
            ]
        }]
    },
)

File and Title exist, but it still doesn't work :/

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\site-packages\notion_client\client.py", line 115, in _parse_response
    response.raise_for_status()
  File "C:\ProgramData\Anaconda3\lib\site-packages\httpx\_models.py", line 1508, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '400 Bad Request' for url 'https://api.notion.com/v1/pages'
For more information check: https://httpstatuses.com/400

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:/Thomas/GitHub/notion-scholar/excluded/upload.py", line 12, in <module>
    client.pages.create(
  File "C:\ProgramData\Anaconda3\lib\site-packages\notion_client\api_endpoints.py", line 202, in create
    return self.parent.request(
  File "C:\ProgramData\Anaconda3\lib\site-packages\notion_client\client.py", line 187, in request
    return self._parse_response(response)
  File "C:\ProgramData\Anaconda3\lib\site-packages\notion_client\client.py", line 122, in _parse_response
    raise APIResponseError(response, body["message"], code)
notion_client.errors.APIResponseError: body failed validation. Fix one: body.properties.File[0].text should be defined, instead was `undefined`. body.properties.File[0].mention should be defined, instead was `undefined`. body.properties.File[0].equation should be defined, instead was `undefined`. body.properties.File[0].id should be defined, instead was `undefined`. body.properties.File[0].name should be defined, instead was `undefined`. body.properties.File[0].person should be defined, instead was `undefined`. body.properties.File[0].bot should be defined, instead was `undefined`. body.properties.File[0].file should be defined, instead was `undefined`. body.properties.File[0].external should be defined, instead was `undefined`.

Process finished with exit code 1

I got what to put for File here https://developers.notion.com/reference/property-value-object#files-property-values

If I do that:

    client = Client(auth=token)
    client.pages.create(
        parent={'database_id': database_id},
        properties={
            'Title': [{'text': {'content': 'Testing upload'}}],
        }
    )

It works just fine

Thanks!

Impossible to clear a field

According to notion support, to clear a field (reset its value), you should do:
{
"properties": {
"Sponsor": { "select": null }
}

This is not possible with the current version. The best that we can do is:
{
"properties": {
"Sponsor": { "select": None }
}

but it is not accepted by notion.

Trying to create a page with an icon, but I get a page...without an icon

Hi!

Just as the title says, I'm trying to create a page with an icon, but I fail to see where I'm going wrong about it. Here's my code:

notion.pages.create(parent={"database_id": projects_db},
                           icon={"emoji": "⚜️", "type":"emoji"},
                           properties={"Nom": {"title": [{"text": {"content": "Blabla"}}]}})

What I get is a page, in the right database, with the right name, but no icon. Am I missing something?

Thanks!

SSL Certificate validation failed.

I am running the package for the first time.
Nice work with this! Interfaces are super pleasant to use.

Unfortunately my socket connection can't be established due to SSL certificate issue.
Any idea how to fix this?

>>> notion = Client(auth="...")
>>> notion.users.list()
Traceback (most recent call last):
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpx/_transports/default.py", line 61, in map_httpcore_exceptions
    yield
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpx/_transports/default.py", line 180, in handle_request
    status_code, headers, byte_stream, extensions = self._pool.handle_request(
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpcore/_sync/connection_pool.py", line 234, in handle_request
    response = connection.handle_request(
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpcore/_sync/connection.py", line 136, in handle_request
    self.socket = self._open_socket(timeout)
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpcore/_sync/connection.py", line 163, in _open_socket
    return self._backend.open_tcp_stream(
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpcore/_backends/sync.py", line 144, in open_tcp_stream
    return SyncSocketStream(sock=sock)
  File "/usr/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/mnt/c/Users/MeillieA/PycharmProjects/notion-code-legacy/.venv/lib/python3.8/site-packages/httpcore/_exceptions.py", line 12, in map_exceptions
    raise to_exc(exc) from None
httpcore.ConnectError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1131) 

Querying databases and pages provide limited properties

This is ultimately 2 issues I believe.

  1. Listing a database where I have 23 properties, the results returned provides only 16
  2. Querying a database to retrieve pages provided only 14 properties where again, there should be 23 properties.
class Test:
    def __init__(self):
        self.notion = None
        self.databases = {}
        self.pageIds = []
        self.pages = {}

    def initialize(self):
        #NOTION_TOKEN = os.getenv("NOTION_TOKEN", "")
        with open('Notion_Config.YAML') as f:
            data = yaml.load(f, Loader=yaml.FullLoader)
            print(data)
        NOTION_TOKEN = data["Token"]

        while NOTION_TOKEN == "":
            print("NOTION_TOKEN not found.")
            NOTION_TOKEN = input("Enter your integration token: ").strip()

        self.notion = Client(auth=NOTION_TOKEN)

    def list_db(self):
        results = self.notion.databases.list()
        print("Listing databases: ")
        for item in results["results"]:
            print(item["title"][0]["plain_text"])
            self.databases.update({item["title"][0]["plain_text"] : item["id"]})

    def query_db(self, database_name):
        #while db["more"] == True:
        db = self.notion.databases.query(database_id=self.databases.get(database_name))
        for item in db["results"]:
            print(item)
            self.pageIds.append(item["id"])

    def query_pages(self):
        for item in self.pageIds:
            page = self.notion.pages.retrieve(page_id=item)
            print(page)

Calling list_db. You can see the number of properties retrieved is 16
image

Calling query_db. You can see the number of properties retrieved is 14
image

This screenshot shows the list of properties for my database
image

Images url not included

Yesterday it was working well. But now the image columns are returned as only name of image but not the url, for both external and uploads.

vscode jupyter os.environ key error

Hi guys, when using vs code with conda envs, I get a key error for loading the Client with my key via
notion = Client(auth=os.environ['TOKEN']
i get this output

KeyError: 'TOKEN'
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/var/folders/cq/tw7v96zx0sx59ptff9z4d6hw0000gn/T/ipykernel_14685/1136862354.py in <module>
----> 1 notion = Client(auth=os.environ["TOKEN"])

~/miniforge3/envs/pipeline/lib/python3.8/os.py in __getitem__(self, key)
    673         except KeyError:
    674             # raise KeyError with the original key value
--> 675             raise KeyError(key) from None
    676         return self.decodevalue(value)
    677 

KeyError: 'TOKEN'

anybody came across the same issue, or knows how to fix it?

Testing notion-sdk-py when part of another project

Hello,
I am using notion-sdk-py for a project, I would like to write tests in order to see if the different functions that are using some modules from notion-sdk-py are working in order to enhance the workflow/stability of my project. However I am not sure how it is possible to test this API as it is linked to a notion database/account.
Do you have recommendation on how to do so ?
Thanks!

Documentation links broken

The given examples have been really helpful. However, most of the links in docs/SUMMARY.md are broken. Is there a link to the full documentation anywhere else?

I would be happy to contribute as well if needed.

TimeoutException should handled during `send()` not in `_parse_reponse`

def _parse_response(self, response: Response) -> Any:
        try:
            response.raise_for_status()
        except httpx.TimeoutException as error:
            if is_timeout_error(error):
                raise RequestTimeoutError()
            raise
        except httpx.HTTPStatusError as error:
            body = error.response.json()
            code = body.get("code")
            if is_api_error_code(code):
                body = APIErrorResponseBody(code=code, message=body["message"])
                raise APIResponseError(error.response, body)
            raise HTTPResponseError(error.response)

        return response.json()

Timeout exception handling logic above in BaseClient._parse_response() would never run, becuase raise_for_status() won't raise TimeoutException.

JSONDecodeError when server return 502

This line will cause JSONDecodeError when server return 502

    def _parse_response(self, response: Response) -> Any:
        try:
            response.raise_for_status()
        except httpx.HTTPStatusError as error:
            body = error.response.json()   # <<< This line
            code = body.get("code")
            if code and is_api_error_code(code):
                raise APIResponseError(response, body["message"], code)
            raise HTTPResponseError(error.response)

It would be better try it first

    def _parse_response(self, response: Response) -> Any:
        try:
            response.raise_for_status()
        except httpx.HTTPStatusError as error:
            try:
                body = error.response.json()
            except:
                raise HTTPResponseError(error.response)
            code = body.get("code")
            if code and is_api_error_code(code):
                raise APIResponseError(response, body["message"], code)
            raise HTTPResponseError(error.response)

Detail: the 502 response content is html, basically saying server error, try again.

Examples on how to use async client

Hi - totally understand if this is super low priority, but would be awesome to see some async client examples in action.

I am not an Async expert but I was trying to get familiar with the AsyncClient but i am unable to do so.

Below my beginners attempt:

async_notion = AsyncClient(auth=NOTION_TOKEN)
async def main():
    await asyncio.gather(async_notion.databases.retrieve(**{"database_id": "123"})), async_notion.databases.retrieve(**{"database_id": "123"}))

asyncio.run(main())

I am getting the following exception:

_asyncio.py in receive(self, max_bytes)

   1091             except IndexError:
   1092                 if self._closed:
-> 1093                     raise ClosedResourceError from None
   1094                 elif self._protocol.exception:
   1095                     raise self._protocol.exception

ClosedResourceError: 

Cheers!

Cannot update icon and cover

The problem

In the new version, Notion added an icon and image cover support and consequently fields in API, but the SDK doesn't support this feature (and fields) yet.

Possible solution

I suppose the problem is here in line 147:

def create(self, **kwargs: Any) -> SyncAsync[Any]:
"""Create a new page in the specified database or as a child of an existing page.
If the parent is a database, the `properties` parameter must conform to the
parent database's property schema.
If the parent is a page, the only valid property is `title`. The new page may
include page content, described as blocks in the `children` parameter.
"""
return self.parent.request(
path="pages",
method="POST",
body=pick(kwargs, "parent", "properties", "children"),
auth=kwargs.get("auth"),
)

I guess It should be changed like that:

# "icon" and "cover" fields were added
body=pick(kwargs, "parent", "properties", "children", "icon", "cover"),

How can I create new page with some contents of text

Hi!

I use the script in the example and success to add one new page with my database.
But I want to add some text on page creating like below.

image

My code shows below.

# Initialize the client
notion = Client(auth=NOTION_TOKEN)

# Create a new page
properties = {
    "Name": {"title": [{"text": {"content": "test_title"}}]},
    "Created": {"date": {"start": "2022-03-28"}},
    "Tags": {
      "type": "multi_select",
      "multi_select": [{"name": "Daily"}]
    },
    "Property": {
        "type": "rich_text",
        "rich_text": [
            {
                "type": "text",
                "text": {"content": ""},
            },
        ], 
    }
}

test_ = notion.pages.create(parent={"database_id": DATABASE_ID}, properties=properties)

Thanks for helping.

I'm confuse with the official documentation when I use a method with requests. :'(

Update to the 2022-02-22 version

Hi!
I see that the API has been updated to the new version, which has a couple of cool features: one I'm interested in is the chance to add advanced types of block like toggles to the BlocksChildrenEndpoint, which is not working with the current version of the library.

If I try to append a children to an existing page with the example code for a toggle provided by Notion I get an error saying to update the API version.

I think that it's solvable by upgrading the version here, but since it's not backward compatible I'm asking here if there's something to be aware of before proceeding.

Missing requirements.txt

notion-sdk-py/setup.py

Lines 20 to 21 in 93f3ca9

install_requires=[
"httpx >= 0.15.0, < 0.18.0",

Install requires httpx, so I think, the project requirements.txt for that.

The best point for using poetry is that we don't need to write the deps twice. Once in requirements.txt, and once in setup.py.

Can't create a page using parent='page_id'

Hi!

I am trying to create a page inside a page but it seems imposible to me. ΒΏIs it supported?

notion.pages.create(parent={"page_id": PAGE_ID},
                   icon={"emoji": "πŸ§ͺ", "type":"emoji"},
                   title={"text": {"content": "Blabla"}})

The output is:

Traceback (most recent call last):
  File "c:\Codigo\Notion-API\Notion-sdk-client-VTC-v1.0.py", line 62, in <module>
    notion.pages.create(parent={"page_id": PAGE_ID},
  File "c:\Codigo\Notion-API\venv\lib\site-packages\notion_client\api_endpoints.py", line 197, in create
    return self.parent.request(
  File "c:\Codigo\Notion-API\venv\lib\site-packages\notion_client\client.py", line 188, in request
    return self._parse_response(response)
  File "c:\Codigo\Notion-API\venv\lib\site-packages\notion_client\client.py", line 120, in _parse_response
    raise APIResponseError(response, body["message"], code)
notion_client.errors.APIResponseError: body failed validation: body.parent.database_id should be defined, instead was `undefined`.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.