GitHub Copilot with PyCharm

Copilot IDE & Developer WorkflowAcademy lesson 21Cluster 2 · Lesson 8 of 12Beginner → Intermediate13 min readVersion-sensitive
Published
Updated
Last technically verified
GitHub Copilot with PyCharmCopilot IDE & Developer Workflow8Beginner → Intermediate/github-copilot/ide/pycharm/

Python is dynamically typed, which changes what Copilot can do for you. In Java, a signature tells the model almost everything. In Python, a function taking data and returning something could be doing anything at all — and the model will guess, plausibly and often wrongly.

This lesson is about closing that gap: giving Copilot the constraints Python does not supply by default, and using PyCharm’s environment awareness to keep answers tied to the packages you actually have.

Copilot support

Copilot in PyCharm (JetBrains plugin)

Verified August 20, 2026

One plugin across the whole JetBrains family. Core features are supported; much of the customisation layer is still in preview.

Installed via
Plugin from the JetBrains Marketplace, then restart and sign in
Extension version tracked
1.5.66
Feature coverage
9 supported · 7 preview · 2 not supported
  • Code completionSupported
  • ChatSupported
  • Agent modeSupported
  • Custom instructionsPreview
  • MCPSupported
  • Copilot code reviewSupported

Setup, briefly

Install GitHub Copilot from the Marketplace inside PyCharm, restart, then Tools → GitHub Copilot → Login to GitHub and complete the device-code flow in your browser.

Type hints do the work

The single most useful thing you can do for Copilot in Python is annotate.

Consider a function that takes records and returns something. Without hints, the model has to infer from the name, the body and whatever surrounding code it can see. With hints:

def summarise_by_region(
    orders: list[Order],
    *,
    include_cancelled: bool = False,
) -> dict[str, Decimal]:

it now knows the input is a list of a specific domain type, that there is a keyword-only flag with a default, and that the return is a mapping of strings to decimals rather than floats. The space of plausible completions has collapsed dramatically, and — importantly — a suggestion that returns floats is now visibly wrong rather than subtly wrong.

Three further habits follow from this:

  • Annotate before asking, not after. Adding hints to a function you are about to have Copilot fill in costs seconds and improves what comes back.
  • Use domain types. CustomerId communicates more than str, even as a NewType.
  • Say what Optional means. str | None in a signature tells the model that absence is expected and must be handled rather than assumed away.

The interpreter problem

Copilot does not know which Python interpreter your project uses, which virtual environment is active, or which packages are installed. PyCharm knows all three; the model does not.

The consequence is familiar: suggestions that import a package you do not have, or that use an API from a version other than yours. Python makes this worse than most ecosystems because the same library name can behave very differently across major versions — Pydantic 1 and 2, SQLAlchemy 1.4 and 2.0, and the whole requests-versus-httpx question are all live in training data simultaneously.

Two mitigations, both cheap.

Keep your dependency file open. pyproject.toml, requirements.txt or environment.yml — whichever your project uses. It states the packages and the pinned versions.

Name the version in the prompt when it matters.

Copilot promptPin the version explicitlyPyCharm chat

Write this model using Pydantic v2 — field validators in the v2 style, not v1. Use only packages already listed in my pyproject.toml. If something is missing, tell me rather than importing it.

Generating pytest tests

/tests produces reasonable pytest output, with the same condition as elsewhere: give it an existing test module to imitate, or you will get whichever style is most common rather than yours.

The specific things worth checking in generated Python tests:

Fixtures. If your project uses conftest.py fixtures, generated tests should use them rather than constructing objects inline. Have conftest.py open.

Parametrisation. @pytest.mark.parametrize is the idiomatic way to cover cases in pytest, and generated tests often write three near-identical functions instead. Ask for it explicitly.

Assertions that cannot fail. The dangerous pattern is a test that asserts a mock was called rather than that the result was right. It passes whether or not the code works.

Copilot promptTests worth keepingPyCharm chat

Write pytest tests for this function, using the fixtures in the conftest.py I have open. Use @pytest.mark.parametrize for the input variations. Every assertion must check a value that would be different if the function were wrong — do not assert only that a mock was called.

Run them in PyCharm’s test runner and read the results. Then do the check that matters: break the function deliberately and confirm the tests fail. A test suite that passes against broken code is worse than none, and this is the fastest way to find out.

Django

Django’s structure is highly conventional, which cuts both ways. The model has seen an enormous amount of Django and produces plausible code readily. It has also seen every Django version since 1.x, and tutorials outnumber production code in that corpus.

Three practical points.

Pin the version. “Django 5.2” in your repository instructions prevents patterns retired several releases ago.

Open the model when asking about a view, and the view when asking about a template. Django’s layers reference each other by name, and a file that does not show the related layer leaves the model to guess field names.

Migrations are not a Copilot task. Generate them with makemigrations and read them. Copilot can usefully explain what a migration will do to existing data, which is a genuinely valuable question:

Copilot promptUnderstand a migration before running itPyCharm chat

Explain what this migration does to existing rows. Identify anything that would fail or lose data on a table that already has several million rows, and say whether it takes a lock that would block writes.

That is a question about consequences, which is where the tool earns its place — as opposed to generating the migration itself, which Django does correctly and Copilot does approximately.

FastAPI and typed frameworks

FastAPI suits Copilot better than most Python frameworks for a structural reason: it is built on type hints, so the constraints are already in the code. A route declaring its Pydantic request and response models has told the model nearly everything about what the handler must do.

The habit that follows is to write the models first, then the route signature, then let completion fill the body. By the time you reach the body, the shape is so constrained that the suggestion is usually close.

Where it still goes wrong: async correctness. A blocking database call inside an async def handler will work in development and fall over under load. The model writes both patterns fluently and cannot tell which library you are using is async. Check every I/O call in an async handler.

Notebooks

PyCharm Professional supports Jupyter notebooks, and Copilot works in them, with a caveat worth stating.

Notebook state is invisible. A cell’s behaviour depends on what ran before it, and — because cells can be executed out of order — on the order in which they ran. Copilot sees the code, not the kernel state, so a suggestion referencing a variable can be right in the notebook you intended and wrong in the kernel you have.

Notebooks also accumulate patterns that would not survive review in a module: mutation of globals, re-defined names, cells that only work once. Generated code inherits the surrounding style. If notebook code is destined for a module, that is a rewrite rather than a copy, and Copilot is useful for it:

Copilot promptPromote notebook code to a modulePyCharm chat

Convert this notebook cell into a module-level function with type hints and no dependence on globals. Every input becomes a parameter. Tell me which values it was relying on from earlier cells.

Data work: pandas, NumPy and the plausible-wrong problem

A large share of Python work is data manipulation, and it is the area where Copilot is simultaneously most impressive and most dangerous.

Impressive, because pandas has a wide API with many ways to express the same operation, and the model knows all of them. Asking for a grouped aggregation with a rolling window produces working code faster than looking up the syntax.

Dangerous, because pandas operations fail quietly. A merge that should have been an inner join done as a left join produces a DataFrame with nulls rather than an error. A groupby that drops NaN keys silently loses rows. Chained assignment may or may not write back. None of this raises; all of it changes your numbers.

The habit that addresses it is to check shape at every step rather than reading the code and deciding it looks right:

Copilot promptAsk for the failure modes, not just the codePyCharm chat

Write this aggregation. Then list, separately, the ways it could silently produce wrong output rather than raising: rows dropped by the join, NaN handling in the grouping keys, and any operation whose result depends on the index. I want the failure modes, not reassurance.

Two supporting habits. Assert the row count you expect after any join, as a line of code rather than an intention — it costs nothing and catches the most common error immediately. And compare aggregates against a known-good figure before trusting a pipeline, whether or not Copilot wrote it.

Scripts, packaging and imports

Python’s import system is a recurring source of generated code that is subtly wrong for your project layout.

The model does not know whether you run your code as a script, as a module with -m, or as an installed package — and the correct import style differs across those. Relative imports that work in a package fail in a script; sys.path manipulation appears in training data far more often than it should.

State the layout when it matters:

Copilot promptTell it how the code runsPyCharm chat

This project is an installed package run with python -m myapp.cli. Use absolute imports rooted at the package name. Do not manipulate sys.path and do not use relative imports beyond a single level.

PyCharm helps here in a way that is easy to overlook: it marks source roots, so unresolved imports are flagged immediately against your actual layout rather than against an assumed one. That is another reason the gutter-check habit matters more in Python than in languages where the compiler would object.

Repository instructions for a Python project

Custom instructions are in public preview in JetBrains. Keep them factual:

# Copilot instructions

## Stack
- Python 3.12, FastAPI 0.115, SQLAlchemy 2.0, Pydantic v2.
- Dependencies are managed with uv; declared in pyproject.toml.
- Tests are pytest with pytest-asyncio. Not unittest.

## Conventions
- Every function has type hints, including the return type.
- Money is Decimal, never float.
- No bare `except:`. Catch the specific exception.
- Async handlers use async database calls only — no blocking I/O.

## Tests
- Use fixtures from conftest.py; do not construct models inline.
- Use @pytest.mark.parametrize for input variations.

Each line is checkable in the output. “Write Pythonic code” is not.

A realistic session

A short, concrete example of the whole thing working together.

You are adding an export endpoint to a FastAPI service: given a date range, it returns a CSV of orders. Nothing conceptually hard, and several places to get it wrong.

Write the models first. A Pydantic request model with the date range and a response model — or, since this returns a file, a documented response type. This is the step that makes everything after it easier, because it states the contract in code the model can read.

Declare the route signature. Path, method, dependencies, return type. At this point the handler body is heavily constrained.

Let completion draft the body, then check the I/O. The specific thing to look for is a blocking database call in an async def handler. It will work when you test it and degrade badly under concurrency, and the model writes both styles fluently.

Ask about the part that is genuinely hard.

Copilot promptThe question worth askingPyCharm chat

This endpoint may return a large result set. Explain what happens to memory if a date range covers a year of orders, and show how to stream the CSV instead of building it in memory — using the streaming approach FastAPI supports rather than a custom generator wrapped around a list.

That is the question that separates a working endpoint from one that survives production, and it is exactly the kind of thing worth asking rather than discovering.

Generate tests with conftest.py open, then break the handler on purpose and confirm they fail.

Read the diff before committing. In PyCharm’s Git tool window, where you see what changed rather than what you meant to change.

Copilot did three useful things there — drafted a constrained body, explained a scaling consequence, and wrote tests. The decisions about contract, layering and correctness stayed with you, which is the arrangement that holds up.

Security

Frequently asked questions

Does Copilot work in PyCharm Community Edition? Yes. Community is on GitHub’s compatibility list.

Why does it suggest packages I have not installed? It cannot see your virtual environment. Keep pyproject.toml or requirements.txt open and PyCharm will flag the unresolved import immediately.

Why is it writing Pydantic v1 code? Because both versions are heavily represented in training data. Name the version in the prompt and in your repository instructions.

Do type hints really change the output? Substantially. They are the closest Python equivalent to the declarations that make typed languages easier for the model, and they also give PyCharm something to check the result against.

Should I let it write migrations? No — generate them with your framework’s tooling. Do ask it to explain what a migration will do to existing data.

Is the plugin different from IntelliJ’s? No, it is the same plugin. The Python-specific parts are habits, not features.

Next steps

Android Studio covers the JetBrains IDE with the most unusual situation, and IntelliJ IDEA covers the same plugin in a Java and Kotlin context.

For the plugin itself — installation, features, preview status — see GitHub Copilot with JetBrains IDEs.

Sources

Every version-sensitive claim on this page was checked against first-party documentation. Only sources actually used are listed.

Primary sources