Prefect 2: AttributeError When Attempting to Create Deployment

I’m in the process of migrating from a Prefect 1.0 cloud deployment to a 2.0 cloud deploying and I’m receiving the error “AttributeError: ‘coroutine’ object has no attribute ‘apply’” when trying to run the hi_deploy.py deployment code example.

hi_deploy.py:

from prefect import flow
from prefect.deployments import Deployment

@flow(log_prints=True)
def hi():
    print("Hi from Prefect!")

def deploy():
    deployment = Deployment.build_from_flow(
        flow=hi,
        name="prefect-example-deployment"
    )
    deployment.apply()

if __name__ == "__main__":
    deploy()

full error context:

 File ~\Anaconda3\envs\prefect\lib\site-packages\spyder_kernels\py3compat.py:356 in compat_exec
    exec(code, globals, locals)

  File ~\untitled0.py:16
    deploy()

  File ~\untitled0.py:13 in deploy
    deployment.apply()

AttributeError: 'coroutine' object has no attribute 'apply'

I’m running the example in new conda environment on a windows 10 machine with the following packages installed:

aiosqlite==0.18.0
alabaster==0.7.12
alembic==1.10.2
anyio==3.6.2
apprise==1.3.0
arrow==1.2.3
asgi-lifespan==2.0.0
asn1crypto==1.5.1
astroid==2.11.7
asttokens==2.0.5
asyncpg==0.27.0
atomicwrites==1.4.0
attrs==22.2.0
autopep8==1.6.0
Babel==2.11.0
backcall==0.2.0
bcrypt==3.2.0
beautifulsoup4==4.12.0
binaryornot==0.4.4
black==22.6.0
bleach==4.1.0
boto3==1.24.28
botocore==1.27.59
Bottleneck==1.3.5
brotlipy==0.7.0
cachetools==5.3.0
certifi==2022.12.7
cffi==1.15.1
chardet==4.0.0
charset-normalizer==3.1.0
click==8.1.3
cloudpickle==2.2.1
colorama==0.4.6
comm==0.1.2
cookiecutter==1.7.3
coolname==2.2.0
croniter==1.3.8
cryptography==39.0.2
dateparser==1.1.7
debugpy==1.5.1
decorator==5.1.1
defusedxml==0.7.1
diff-match-patch==20200713
dill==0.3.6
docker==6.0.1
docstring-to-markdown==0.11
docutils==0.18.1
ecdsa==0.17.0
entrypoints==0.4
et-xmlfile==1.1.0
executing==0.8.3
fastapi==0.95.0
fastjsonschema==2.16.2
flake8==6.0.0
flit_core==3.6.0
fsspec==2023.1.0
google-auth==2.16.2
greenlet==2.0.2
griffe==0.25.5
h11==0.14.0
h2==4.1.0
hpack==4.0.0
httpcore==0.16.3
httpx==0.23.3
hyperframe==6.0.1
idna==3.4
imagesize==1.4.1
importlib-metadata==4.11.3
inflection==0.5.1
intervaltree==3.1.0
ipykernel==6.19.2
ipython==8.8.0
ipython-genutils==0.2.0
isort==5.9.3
jedi==0.18.1
jellyfish==0.9.0
Jinja2==3.1.2
jinja2-time==0.2.0
jmespath==0.10.0
jsonpatch==1.32
jsonpointer==2.3
jsonschema==4.17.3
jupyter_client==7.4.9
jupyter_core==5.1.1
jupyterlab-pygments==0.1.2
keyring==23.4.0
kubernetes==26.1.0
lazy-object-proxy==1.6.0
lxml==4.9.1
Mako==1.2.4
Markdown==3.4.1
markdown-it-py==2.2.0
MarkupSafe==2.1.2
matplotlib-inline==0.1.6
mccabe==0.7.0
mdurl==0.1.2
mistune==0.8.4
mkl-fft==1.3.1
mkl-random==1.2.2
mkl-service==2.4.0
mypy-extensions==0.4.3
nbclient==0.5.13
nbconvert==6.5.4
nbformat==5.7.0
nest-asyncio==1.5.6
numexpr==2.8.4
numpy==1.23.5
numpydoc==1.5.0
oauthlib==3.2.2
openpyxl==3.0.10
orjson==3.8.8
packaging==23.0
pandas==1.5.3
pandocfilters==1.5.0
paramiko==2.8.1
parso==0.8.3
pathspec==0.11.1
pendulum==2.1.2
pexpect==4.8.0
pickleshare==0.7.5
pip==23.0.1
platformdirs==2.5.2
plotly==5.9.0
pluggy==1.0.0
ply==3.11
poyo==0.5.0
prefect==2.8.6
prompt-toolkit==3.0.36
psutil==5.9.0
psycopg2==2.9.3
ptyprocess==0.7.0
pure-eval==0.2.2
pyarrow==8.0.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycodestyle==2.10.0
pycparser==2.21
pydantic==1.10.6
pydocstyle==6.3.0
pyflakes==3.0.1
Pygments==2.14.0
pylint==2.14.5
pylint-venv==2.3.0
pyls-spyder==0.4.0
PyNaCl==1.5.0
pyodbc==4.0.34
pyOpenSSL==23.0.0
PyQt5==5.15.7
PyQt5-sip==12.11.0
PyQtWebEngine==5.15.4
pyrsistent==0.19.3
PySocks==1.7.1
python-dateutil==2.8.2
python-dotenv==1.0.0
python-http-client==3.3.2
python-lsp-black==1.2.1
python-lsp-jsonrpc==1.0.0
python-lsp-server==1.7.1
python-slugify==8.0.1
pytoolconfig==0.0.0
pytz==2022.7.1
pytz-deprecation-shim==0.1.0.post0
pytzdata==2020.1
pywin32==305.1
pywin32-ctypes==0.2.0
PyYAML==6.0
pyzmq==23.2.0
QDarkStyle==3.0.2
qstylizer==0.2.2
QtAwesome==1.2.2
qtconsole==5.4.0
QtPy==2.2.0
readchar==4.0.5
redshift-connector==2.0.910
regex==2022.10.31
requests==2.28.2
requests-oauthlib==1.3.1
rfc3986==1.5.0
rich==13.3.2
rope==1.7.0
rsa==4.9
Rtree==1.0.1
s3transfer==0.6.0
scramp==1.4.4
sendgrid==6.7.1
setuptools==65.6.3
sip==6.6.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
sortedcontainers==2.4.0
soupsieve==2.3.2.post1
Sphinx==5.0.2
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
spyder==5.4.2
spyder-kernels==2.4.2
SQLAlchemy==1.4.47
stack-data==0.2.0
starkbank-ecdsa==1.1.1
starlette==0.26.1
tenacity==8.0.1
text-unidecode==1.3
textdistance==4.2.1
three-merge==0.1.1
tinycss2==1.2.1
toml==0.10.2
tomli==2.0.1
tomlkit==0.11.1
tornado==6.2
traitlets==5.7.1
typer==0.7.0
typing_extensions==4.5.0
tzdata==2022.7
tzlocal==4.3
ujson==5.4.0
Unidecode==1.2.0
urllib3==1.26.15
uvicorn==0.21.1
watchdog==2.1.6
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.5.1
websockets==10.4
Werkzeug==2.2.2
whatthepatch==1.0.2
wheel==0.38.4
win-inet-pton==1.1.0
wincertstore==0.2
wrapt==1.14.1
XlsxWriter==3.0.3
yapf==0.31.0
zipp==3.15.0

When inspecting the contents of the ~\Lib\site-packages\prefect\deployments.py file I can see that the apply attribute is definitely present in the Deployment Class


@experimental_field(
    "work_pool_name",
    group="work_pools",
    when=lambda x: x is not None and x != DEFAULT_AGENT_WORK_POOL_NAME,
)
class Deployment(BaseModel):
   
    # ........................ truncated ..........................
    @sync_compatible
    async def apply(
        self, upload: bool = False, work_queue_concurrency: int = None
    ) -> UUID:
        """
        Registers this deployment with the API and returns the deployment's ID.

        Args:
            upload: if True, deployment files are automatically uploaded to remote storage
            work_queue_concurrency: If provided, sets the concurrency limit on the deployment's work queue
        """
        if not self.name or not self.flow_name:
            raise ValueError("Both a deployment name and flow name must be set.")
        async with get_client() as client:
            # prep IDs
            flow_id = await client.create_flow_from_name(self.flow_name)

            infrastructure_document_id = self.infrastructure._block_document_id
            if not infrastructure_document_id:
                # if not building off a block, will create an anonymous block
                self.infrastructure = self.infrastructure.copy()
                infrastructure_document_id = await self.infrastructure._save(
                    is_anonymous=True,
                )

            if upload:
                await self.upload_to_storage()

            if self.work_queue_name and work_queue_concurrency is not None:
                try:
                    res = await client.create_work_queue(
                        name=self.work_queue_name, work_pool_name=self.work_pool_name
                    )
                except ObjectAlreadyExists:
                    res = await client.read_work_queue_by_name(
                        name=self.work_queue_name, work_pool_name=self.work_pool_name
                    )
                await client.update_work_queue(
                    res.id, concurrency_limit=work_queue_concurrency
                )

            # we assume storage was already saved
            storage_document_id = getattr(self.storage, "_block_document_id", None)
            deployment_id = await client.create_deployment(
                flow_id=flow_id,
                name=self.name,
                work_queue_name=self.work_queue_name,
                work_pool_name=self.work_pool_name,
                version=self.version,
                schedule=self.schedule,
                is_schedule_active=self.is_schedule_active,
                parameters=self.parameters,
                description=self.description,
                tags=self.tags,
                manifest_path=self.manifest_path,  # allows for backwards YAML compat
                path=self.path,
                entrypoint=self.entrypoint,
                infra_overrides=self.infra_overrides,
                storage_document_id=storage_document_id,
                infrastructure_document_id=infrastructure_document_id,
                parameter_openapi_schema=self.parameter_openapi_schema.dict(),
            )

            return deployment_id
    # ........................ truncated ..........................
        if apply:
            await deployment.apply()

        return deployment

I think I found my issue, or at least a work around… It looks like the Prefect 2.0 deployment script will not run properly when trying to execute it in the Spyder IDE (version 5.4.2), as was possible when trying to register a flow with flow.register(project_name = “gather_shells”) in Prefect 1.0

I was able to run the deployment script and register the project from the CLI. It would be nice to be able to register the project directly from an IDE.