# core


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Table of contents

`_fbf3cf89` [**Module setup**](#module-setup)  
`_1d82f33e` - [Imports](#imports)  
`_aa5fa7c7` - [GitHub API](#github-api)  
`_d476a2e5` - [Verbosity](#verbosity)  
`_1d1c6a8c` - [SolveIT domain](#solveit-domain)  
`_b5d7fcb4` - [Output](#output)  
`_2d4d75ba` –
[\[`Result`\](https://1iis.github.io/pj/core.html#result)](#result)  
`_50f813c0` – [`Lines(L)`](#linesl)  
`_efbdf781` – [`FileList(Lines)`](#filelistlines)  
`_0db6a50a` [**Class
\[`Project`\](https://1iis.github.io/pj/core.html#project)**](#class-project)  
`_b93d1a6a` [**`Project.new()`**](#projectnew)  
`_a17b0188`
[**\[`Project.sync()`\](https://1iis.github.io/pj/core.html#project.sync)**](#projectsync)  
`_f9c8ed1e`
[**\[`Project.ship()`\](https://1iis.github.io/pj/core.html#project.ship)**](#projectship)  
`_0ecb7e6e`
[**\[`Project.ls()`\](https://1iis.github.io/pj/core.html#project.ls)**](#projectls)  
`_e908abfe` [**Helpers**](#helpers)  
`_9d5246cf` - [Magic methods](#magic-methods)  
`_9e719823` – [`__getattr__`](#__getattr__)  
`_fd836d45` – [`__dir__`](#__dir__)  
`_6c8717a7` – [`_repr_markdown_`](#_repr_markdown_)  
`_480bb662` - [Detection](#detection)  
`_65aba5ea` – [Owner, repo](#owner-repo)  
`_846e8d38` – [Project type](#project-type)  
`_bf0c525a` – [SolveIT URL](#solveit-url)  
`_1c555637` - [Version](#version)  
`_671c0239` - [GH Pages branch select](#gh-pages-branch-select)  
`_a84a675d` - [Quarto `dark`|`light`](#quarto-darklight)  
`_294a4299` [**TODO**](#todo)

See also: [README](https://1iis.github.io/pj/), [beginner help]().

## Module setup

### Imports

### GitHub API

Module-level `_default_api`: call `set_api(GhApi())` once; it’s shared
by all [`Project`](https://1iis.github.io/pj/core.html#project)
instances.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L22"
target="_blank" style="float:right; font-size:smaller">source</a>

### set_api

``` python

def set_api(
    api:ghapi.core.GhApi | None
):

```

*Set the default GhApi instance for all Project instances.*

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L27"
target="_blank" style="float:right; font-size:smaller">source</a>

### get_api

``` python

def get_api(
    
)->ghapi.core.GhApi | None:

```

*Get the current default GhApi instance.*

### Verbosity

Set verbosity once per dialog: `0`=quiet, `1`=normal (default),
`2`=verbose.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L33"
target="_blank" style="float:right; font-size:smaller">source</a>

### set_verbosity

``` python

def set_verbosity(
    v:int
):

```

### SolveIT domain

Cached at import for building file browser URLs.

### Output

#### [`Result`](https://1iis.github.io/pj/core.html#result)

Methods return a [`Result`](https://1iis.github.io/pj/core.html#result)
instance wrapping the value. - Value is “**stdout**”, composable. -
Display is “**stderr**” (`_repr_markdown_`): human status,
notebook-friendly. -
<details>

<summary>

To ease composition, <code>Result.\_\_getattr\_\_</code> forwards to
<code>.val</code>.
</summary>

<p>

<code>Result</code> is invisible: <code>Project.new(“foo”).sync()</code>
chains naturally.
</p>

</details>

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L39"
target="_blank" style="float:right; font-size:smaller">source</a>

### Result

``` python

def Result(
    val, ok:bool=True, msg:str=''
):

```

*Initialize self. See help(type(self)) for accurate signature.*

#### `Lines(L)`

Base class for pretty list output. Inherits from `L` for composition;
`_repr_markdown_` joins items as lines.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L50"
target="_blank" style="float:right; font-size:smaller">source</a>

### Lines

``` python

def Lines(
    items:NoneType=None, rest:VAR_POSITIONAL, use_list:bool=False, match:NoneType=None
):

```

*L subclass with markdown repr that joins lines.*

#### `FileList(Lines)`

Returned by `ls()`. Inherits from `L` so it’s iterable, indexable, and
composable (`for f in pj.ls()`, `.filter()`, etc.), while
`_repr_markdown_` provides rich notebook display with SolveIT links for
notebooks.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L57"
target="_blank" style="float:right; font-size:smaller">source</a>

### FileList

``` python

def FileList(
    items, base_path, domain:NoneType=None
):

```

*L subclass with markdown repr that joins lines.*

## Class [`Project`](https://1iis.github.io/pj/core.html#project)

Each [`Project`](https://1iis.github.io/pj/core.html#project) instance
represents a repository. Auto-detects owner and repo from the git remote
URL. If set, `org` overrides owner.

> \[!TIP\] Existing projects are loaded by instanciating the class with
> a path (`p = Project('path/to/repo')`).  
> New ones should be created with `p = Project.new()`.
>
> Both return a [`Project`](https://1iis.github.io/pj/core.html#project)
> ready to use.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L72"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project

``` python

def Project(
    path:str | pathlib.Path, api:ghapi.core.GhApi | None=None, org:str | None=None
):

```

*Manage a project’s lifecycle: init, sync, ship.*

## `Project.new()`

Creates everything from scratch: GitHub repo via API, local clone, nbdev
hooks installed. If `nbdev=True`, runs scaffolding and applies dark
theme. Initial commit, push, and Pages setup.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L84"
target="_blank" style="float:right; font-size:smaller">source</a>

### new

``` python

def new(
    cls, name:str, # Repository name
    path:str | pathlib.Path | None=None, # Local path (defaults to ./name)
    api:ghapi.core.GhApi | None=None, # GitHub API instance
    org:str | None=None, # Organization (overrides authenticated user)
    desc:str | None=None, # Repository description
    private:bool=True, # Create private repository
    nbdev:bool=False, # Initialize as nbdev project
)->Project:

```

*Create a new GitHub repo, clone it, and return a Project instance.*

## [`Project.sync()`](https://1iis.github.io/pj/core.html#project.sync)

`nbdev_prepare` first for notebooks, then Git `add` all, `commit`,
`pull --rebase`, and `push`.  
We use `rebase` for a clean, linear log (no merge commit) whenever
there’s no conflict (e.g. one may edit different files on different
hosts, and commit all later).

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L125"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project.sync

``` python

def sync(
    msg:str='sync', # Git commit message
)->Result:

```

*Commit all, pull –rebase, push. Runs nbdev_prepare if nbdev project.*

## [`Project.ship()`](https://1iis.github.io/pj/core.html#project.ship)

Full release cycle, auto-detecting strategy by project type. Won’t ship
with uncommitted changes unless `force=True`.

<table>
<colgroup>
<col style="width: 23%" />
<col style="width: 23%" />
<col style="width: 27%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr>
<th>Project Type</th>
<th>Version Bump</th>
<th>Build &amp; Upload</th>
<th>Tag &amp; Release</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>nbdev</strong></td>
<td><code>nbdev_bump_version</code></td>
<td><code>nbdev_pypi</code></td>
<td>git tag + <code>ghapi</code></td>
</tr>
<tr>
<td><strong>python</strong></td>
<td>manual in <code>pyproject.toml</code></td>
<td><code>build</code> + <code>twine</code></td>
<td>git tag + <code>ghapi</code></td>
</tr>
<tr>
<td><strong>other</strong></td>
<td>n/a</td>
<td>n/a</td>
<td>git tag + <code>ghapi</code></td>
</tr>
</tbody>
</table>

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L138"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project.ship

``` python

def ship(
    part:int=2, # Version part to bump: 0=major, 1=minor, 2=patch
    dry_run:bool=False, # Show what would happen without executing
    force:bool=False, # Ship even with uncommitted changes
    pypi:bool=False, # Upload to PyPI
    quiet:bool=False, # Suppress build output
)->Result:

```

*Bump version, build, upload to PyPI, tag, and create GitHub release.*

## [`Project.ls()`](https://1iis.github.io/pj/core.html#project.ls)

List repo files with smart defaults. Shortcuts: `'nbs'`, `'py'`; or pass
regex. Notebooks get SolveIT links.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L178"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project.ls

``` python

def ls(
    pattern:str='', # Shortcut ('nbs', 'py') or regex
    exclude:list=None, # Paths to exclude
)->FileList:

```

*List files in repo, optionally filtered. Returns FileList.*

## Helpers

### Magic methods

#### `__getattr__`

Forward unknown method calls to `self.g` (the `Git` instance), so
`p.status()` is `p.g.status()` (i.e. `git status`).

#### `__dir__`

Extend tab-completion to include `Git` methods. Without this, the editor
wouldn’t know `p.status` exists.

#### `_repr_markdown_`

Called by Jupyter/SolveIT when displaying an object: return a markdown
string showing project info.

### Detection

#### Owner, repo

`_parse_remote()` extracts `(owner, repo)` from the git remote URL,
handling both HTTPS and SSH formats.

#### Project type

`_detect_type()` checks which config files exist: `settings.ini` means
nbdev, `pyproject.toml` means standard Python, otherwise other.  
Used to make property `Project.pjtype`.

#### SolveIT URL

Returns the SolveIT file browser URL for this project, or `None` if not
in SolveIT.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L247"
target="_blank" style="float:right; font-size:smaller">source</a>

### solveit_url

``` python

def solveit_url(
    
):

```

### Version

### GH Pages branch select

For nbdev: - `_ensure_pages()` configures GitHub Pages to deploy from
the `gh-pages` branch: polls for existence (waiting for Actions), then
calls the API.  
- `setup_pages()` for manual call.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L311"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project.setup_pages

``` python

def setup_pages(
    
)->Result:

```

*Manually configure GitHub Pages. Useful for debugging.*

### Quarto `dark`|`light`

Quarto feature: auto-select `light` or `dark` theme based on user system
settings, and display a manual toggle button.

1.  Modify `nbs/_quarto.yml` to use a `light`|`dark` theme pair.

    ``` yaml
        theme:
          light: cosmo
          dark: [cosmo, dark.scss]
    ```

2.  Create `nbs/dark.scss` to produce a dark themed nbdev style, with
    readable font colors for code blocks.

------------------------------------------------------------------------

<a href="https://github.com/1iis/pj/blob/main/pj/core.py#L373"
target="_blank" style="float:right; font-size:smaller">source</a>

### Project.dark_theme

``` python

def dark_theme(
    
)->Result:

```

*Apply dark mode theme to nbdev docs.*

## TODO

- We may extend `__getattr__` to forward to the GitHub API (`GhApi`),
  after `Git` (collisions are rare, hopefully), if/when people want more
  GH API access.  
  Alternatively, we’d build more `pj` methods to handle the most common
  use-cases (like PRs, reviews, merge, etc.) right from a project’s
  dialog.

- better general outputs

  - better messages for
    [`Project`](https://1iis.github.io/pj/core.html#project) methods.
  - interactive HTML menu for some of them, with dropdown menus, spoiler
    folders, info, all the cool stuff.
    - fence that with args, we shouldn’t always force such rich stuff.

- richer `ls()` output with links to all the things: source on GH, doc
  page, SolveIT file editor, Codespaces, VSCode (local), whatever we can
  do to make it super nice to use.

  - make it optional, with great defaults
  - see user.cfg

- see if we can use L elsewhere now that we’re loading it! :D

- think about externalizing the whole display part to its own nb

  - or even its own module, idk. Depends how this generalizes to what,
    how we want to use it, etc.

- user.cfg

  - some way to have personal settings for all the things, default
    values, etc.
  - lets you tell pj what you have, what you do, how, etc.
  - e.g. `py` files should open in `...`, whereas X in Y, etc.
  - see if maybe there are nice online tools to use (e.g. nice renderers
    for file types like ?ML, cool tools to manipulate stuff)
  - this isn’t pj specific, so we should have instance-wide user.cfg;
    then overriding additions of all `user.cfg` files in subdirs (such
    that each dir inherits from configs between it and root).
  - plug CRAFT and TEMPLATE deploy from central repo/repos (and make
    that repo, btw!)

- in-dialog editor! (IDE!!! LOL)

  - either an `<iframe>` if we can help it (with the SolveIT editor, or
    whatever else we can do)
  - or a sol-flow:
    1.  run fn to make cell with file content (type raw, note, code;
        depending on file format)
    2.  edit cell (optionally dup it)
    3.  run fn to take content from CELL ABOVE and write to file
        - optional “backup” arg to copy first to `old.file` before
          writing it
        - optional output of result (run first fn to make cell below)
    4.  optional:
        - diff before writing
        - diff after writing (if backup)
        - use Markdown ```` ``` ```` to fence and render code properly
          when not python (alt. to raw)
        - clean up: fence with XML raws, confirm, delete. (user-chosen
          and never surprising)
