Spec-Zone.ru › Julia 1.1

Загрузка кода

Примечание

В этом разделе рассматриваются технические детали загрузки пакетов. Для установки пакетов используйте Pkg, встроенный менеджер пакетов Julia, чтобы добавить пакеты в активную среду. Для использования пакетов, уже имеющихся в вашей активной среде, запишите import X или using X, как описано в документации по модулям.

Определения

Julia имеет два механизма загрузки кода:

  1. Включение кода: например, include("source.jl"). Включение позволяет разделить одну программу на несколько исходных файлов. Выражение include("source.jl") вызывает оценку содержимого файла source.jl в глобальной области видимости модуля, где вызов include происходит. Если вызов include("source.jl") выполняется несколько раз, source.jl оценивается несколько раз. Путь включения, source.jl, интерпретируется относительно файла, где происходит вызов include. Это упрощает перемещение поддерева исходных файлов. В REPL пути включения интерпретируются относительно текущей рабочей директории, pwd().
  2. Загрузка пакетов: например, import X или using X. Механизм импорта позволяет загрузить пакет — т.е. независимую, повторно используемую коллекцию кода Julia, заключённую в модуль, — и делает полученный модуль доступным под именем X внутри импортирующего модуля. Если один и тот же пакет X импортируется несколько раз в одной сессии Julia, он загружается только в первый раз — при последующих импортах импортирующий модуль получает ссылку на тот же модуль. Обратите внимание, что import X может загружать разные пакеты в разных контекстах: X может ссылаться на один пакет с именем X в основном проекте, но потенциально на разные пакеты также с именем X в каждой зависимости. Подробнее об этом ниже.

Включение кода достаточно прямолинейно и просто: оно оценивает заданный исходный файл в контексте вызывающего объекта. Загрузка пакетов построена на основе включения кода и служит различной цели. Остальная часть этого раздела посвящена поведению и механике загрузки пакетов.

Пакет — это дерево исходных кодов со стандартной структурой, предоставляющее функциональность, которую можно повторно использовать в других проектах Julia. Пакет загружается с помощью import X или using X инструкций. Эти инструкции также делают доступным модуль с именем X — результат загрузки кода пакета — внутри модуля, где происходит инструкция импорта. Значение X в import X зависит от контекста: какой пакет X загружается, зависит от того, в каком коде находится инструкция. Таким образом, обработка import X происходит в два этапа: во-первых, определяется, какой пакет определён как X в данном контексте; во-вторых, определяется, где находится этот конкретный пакет X.

Эти вопросы решаются путем поиска в проектных средах, перечисленных в LOAD_PATH, для файлов проекта (Project.toml или JuliaProject.toml), файлов манифеста (Manifest.toml или JuliaManifest.toml) или папок исходных файлов.

Федерация пакетов

Большинство пакетов однозначно идентифицируются по имени. Однако иногда проект может столкнуться с ситуацией, когда ему нужно использовать два разных пакета с одинаковым именем. Хотя можно решить это, переименовав один из пакетов, это может сильно нарушить работу большого объёма совместного кода. Вместо этого механизм загрузки кода Julia позволяет одному имени пакета ссылаться на разные пакеты в различных компонентах приложения.

Julia поддерживает федеративное управление пакетами, что означает, что несколько независимых сторон могут поддерживать публичные и приватные пакеты и реестры пакетов, а проекты могут зависеть от комбинации публичных и приватных пакетов из различных реестров. Пакеты из различных реестров устанавливаются и управляются с помощью общего набора инструментов и рабочих процессов. Менеджер пакетов Pkg , входящий в состав Julia, позволяет устанавливать и управлять зависимостями ваших проектов. Он помогает создавать и изменять файлы проектов (которые описывают другие проекты, от которых зависит ваш проект), и файлы манифестов (которые делают моментальную фотографию точных версий всей вашей зависимости).

Одним из последствий федерации является то, что не может быть центрального органа для присвоения имён пакетам. Разные субъекты могут использовать одно и то же имя для обозначения не связанных пакетов. Эта возможность неизбежна, поскольку эти субъекты не координируются и могут даже не знать о существовании друг друга. Из-за отсутствия центрального органа по присвоению имён один проект может в конечном итоге зависеть от разных пакетов с одинаковым именем. Механизм загрузки пакетов Julia не требует, чтобы имена пакетов были глобально уникальными, даже в пределах графа зависимостей одного проекта. Вместо этого пакеты идентифицируются с помощью универсально уникальных идентификаторов (UUID), которые присваиваются при создании каждого пакета. Обычно вам не придётся работать напрямую с этими несколько громоздкими 128-битными идентификаторами, так как Pkg позаботится о их генерации и отслеживании за вас. Однако эти UUID предоставляют окончательный ответ на вопрос: "к какому пакету ссылается X?"

Поскольку проблема децентрализованного именования несколько абстрактна, может помочь рассмотреть конкретный сценарий, чтобы понять проблему. Предположим, вы разрабатываете приложение под названием App, которое использует два пакета: Pub и Priv. Priv — это приватный пакет, который вы создали, а Pub — это публичный пакет, который вы используете, но не контролируете. Когда вы создали Priv, не было публичного пакета с именем Priv. Однако впоследствии был опубликован и стал популярным несвязанный пакет с именем Priv . В действительности, пакет Pub начал его использовать. Поэтому при следующем обновлении Pub для получения последних исправлений ошибок и функций App будет зависеть от двух разных пакетов с именем Priv — без вашего участия, кроме обновления. App имеет прямую зависимость от вашего приватного пакета Priv и косвенную зависимость через Pub от нового публичного пакета Priv . Поскольку эти два пакета Priv различны, но оба необходимы для правильной работы App, выражение import Priv должно ссылаться на разные пакеты Priv в зависимости от того, выполняется ли оно в коде App или в коде Pub. Для решения этой проблемы механизм загрузки пакетов Julia различает два пакета Priv по их UUID и выбирает правильный на основе контекста (модуль, который вызвал import). Как происходит это различие, определяется средами, как объяснено в следующих разделах.

Среды

Среда определяет, что означают import X и using X в различных контекстах кода и какие файлы эти инструкции вызывают для загрузки. Julia понимает два типа сред:

  1. Среда проекта — это директория с файлом проекта и необязательным файлом манифеста и образует явную среду. Файл проекта определяет, каковы имена и идентификаторы прямых зависимостей проекта. Файл манифеста, если он есть, даёт полный граф зависимостей, включая все прямые и косвенные зависимости, точные версии каждой зависимости и достаточную информацию для поиска и загрузки правильной версии.
  2. Директория пакета — это директория, содержащая дерево исходных кодов набора пакетов в виде поддиректорий, и образует неявную среду. Если X является поддиректорией директории пакета и X/src/X.jl существует, то пакет X доступен в среде директории пакета, и X/src/X.jl — это исходный файл, с помощью которого он загружается.

Эти среды могут быть объединены для создания многослойной среды: упорядоченного набора проектных сред и папок пакетов, наложенных друг на друга для создания одной составной среды. Например, путь загрузки Julia образует многослойную среду.

Эти среды служат различным целям:

  • Проектные среды обеспечивают воспроизводимость. Занеся проектную среду в систему контроля версий (например, в репозиторий git) вместе с остальным исходным кодом проекта, можно воспроизвести точное состояние проекта и всех его зависимостей. В частности, файл манифеста фиксирует точную версию каждой зависимости, идентифицированную криптографическим хэшем её дерева исходного кода, что позволяет Pkg извлекать правильные версии и быть уверенным, что вы запускаете точно тот же код, что был записан для всех зависимостей.
  • Директории пакетов обеспечивают удобство, когда полная тщательно отслеживаемая проектная среда не нужна. Они полезны, когда вы хотите разместить набор пакетов где-то и напрямую использовать их, не создавая для них проектную среду.
  • Многослойные среды позволяют добавлять инструменты в основную среду. Вы можете добавить среду с инструментами разработки в конец стека, чтобы сделать их доступными из REPL и скриптов, но не изнутри пакетов.

На высоком уровне каждая среда концептуально определяет три карты: корни, граф и пути. При разрешении значения import X карты корней и графа используются для определения идентичности X, а карта путей используется для поиска исходного кода X.

END_OF_DOCUMENT_MARKER
  • roots: name::Symbol ⟶ uuid::UUID

    Карта корней среды сопоставляет имена пакетов с UUID для всех зависимостей верхнего уровня, предоставляемых средой основному проекту (т.е. тех, которые могут быть загружены в Main). Когда Julia встречает import X в основном проекте, она ищет идентификатор X как roots[:X].

  • graph: context::UUID ⟶ name::Symbol ⟶ uuid::UUID

    Граф среды — это многоуровневая карта, которая для каждого context UUID сопоставляет карту из имён к UUID, подобно карте корней, но специфично для этого context. Когда Julia видит import X в коде пакета, UUID которого context, она ищет идентификатор X как graph[context][:X]. В частности, это означает, что import X может ссылаться на разные пакеты в зависимости от context.

  • paths: uuid::UUID × name::Symbol ⟶ path::String

    Карта путей сопоставляет каждой паре UUID-имя пакета местоположение файла исходного кода точки входа этого пакета. После того, как идентификатор X в import X был решён как UUID через корни или граф (в зависимости от того, загружается ли он из основного проекта или зависимости), Julia определяет, какой файл загрузить, чтобы получить X, посмотрев paths[uuid,:X] в среде. Этот файл должен определять модуль с именем X. После загрузки этого пакета любой последующий импорт, разрешающий ту же самую uuid, создаст новую привязку к уже загруженному модулю пакета.

Каждый тип среды определяет эти три карты по-разному, как подробно описано в следующих разделах.

Примечание

Для простоты понимания примеры в этой главе показывают полные структуры данных для корней, графа и путей. Однако код загрузки пакетов Julia не создаёт их явно. Вместо этого он лениво вычисляет только столько каждой структуры, сколько необходимо для загрузки данного пакета.

Среды проектов

Среда проекта определяется директорией, содержащей файл проекта, называемый Project.toml, и необязательно файл манифеста, называемый Manifest.toml. Эти файлы также могут называться JuliaProject.toml и JuliaManifest.toml, в этом случае Project.toml и Manifest.toml игнорируются. Это позволяет сосуществовать с другими инструментами, которые могут считать файлы, называемые Project.toml и Manifest.toml значимыми. Однако для чистых проектов Julia предпочтительнее имена Project.toml и Manifest.toml.

Карты корней, графа и путей среды проекта определяются следующим образом:

Карта корней среды определяется содержимым файла проекта, конкретно его записями name и uuid и его разделом [deps] (все необязательно). Рассмотрим следующий пример файла проекта для гипотетического приложения App, как описано ранее:

name = "App"
uuid = "8f986787-14fe-4607-ba5d-fbff2944afa9"

[deps]
Priv = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
Pub  = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"

Этот файл проекта подразумевает следующую карту корней, если она представлялась в виде словаря Julia:

roots = Dict(
    :App  => UUID("8f986787-14fe-4607-ba5d-fbff2944afa9"),
    :Priv => UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"),
    :Pub  => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
)

Исходя из этой карты корней, в коде App оператор import Priv заставит Julia найти roots[:Priv], что даст ba13f791-ae1d-465a-978b-69c3ad90f72b, UUID пакета Priv, который должен быть загружен в этом контексте. Этот UUID определяет, какой пакет Priv загрузить и использовать при оценке import Priv основным приложением.

Граф зависимостей среды проекта определяется содержимым файла манифеста, если он есть. Если файла манифеста нет, граф пуст. Файл манифеста содержит строку для каждой прямой или косвенной зависимости проекта. Для каждой зависимости файл перечисляет UUID пакета и хеш дерева исходного кода или явный путь к исходному коду. Рассмотрим следующий пример файла манифеста для App:

[[Priv]] # the private one
deps = ["Pub", "Zebra"]
uuid = "ba13f791-ae1d-465a-978b-69c3ad90f72b"
path = "deps/Priv"

[[Priv]] # the public one
uuid = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
git-tree-sha1 = "1bf63d3be994fe83456a03b874b409cfd59a6373"
version = "0.1.5"

[[Pub]]
uuid = "c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"
git-tree-sha1 = "9ebd50e2b0dd1e110e842df3b433cb5869b0dd38"
version = "2.1.4"

  [Pub.deps]
  Priv = "2d15fe94-a1f7-436c-a4d8-07a9a496e01c"
  Zebra = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"

[[Zebra]]
uuid = "f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"
git-tree-sha1 = "e808e36a5d7173974b90a15a353b564f3494092f"
version = "3.4.2"

Этот файл манифеста описывает возможный полный граф зависимостей для проекта App:

  • Существует два разных пакета с именем Priv , которые использует приложение. Оно использует частный пакет, который является корневой зависимостью, и общедоступный, который является косвенной зависимостью через Pub. Они различаются своими уникальными UUID и имеют разные deps:
    • Частный пакет Priv зависит от пакетов Pub и Zebra.
    • Общедоступный пакет Priv не имеет зависимостей.
  • Приложение также зависит от пакета Pub , который в свою очередь зависит от общедоступного пакета Priv и того же пакета Zebra , от которого зависит частный пакет Priv.

Этот граф зависимостей, представленный как словарь, выглядит так:

graph = Dict(
    # Priv – the private one:
    UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b") => Dict(
        :Pub   => UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"),
        :Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
    ),
    # Priv – the public one:
    UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c") => Dict(),
    # Pub:
    UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1") => Dict(
        :Priv  => UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"),
        :Zebra => UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"),
    ),
    # Zebra:
    UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62") => Dict(),
)

Исходя из этой зависимости graph, когда Julia видит import Priv в пакете Pub, UUID которого c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1, она ищет:

graph[UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1")][:Priv]

и получает 2d15fe94-a1f7-436c-a4d8-07a9a496e01c, что указывает, что в контексте пакета Pub import Priv относится к общедоступному пакету Priv , а не к частному, от которого непосредственно зависит приложение. Таким образом, имя Priv может ссылаться на разные пакеты в основном проекте, чем в одной из зависимостей пакета, что позволяет иметь дублирующиеся имена в экосистеме пакетов.

Что произойдёт, если import Zebra будет вычислено в основном коде App? Поскольку Zebra не появляется в файле проекта, импорт завершится неудачей, даже если Zebra есть в файле манифеста. Кроме того, если import Zebra встречается в общедоступном пакете Priv , UUID которого 2d15fe94-a1f7-436c-a4d8-07a9a496e01c, то это также завершится неудачей, так как этот пакет Priv не имеет заявленных зависимостей в файле манифеста и поэтому не может загружать какие-либо пакеты. Пакет Zebra может загружаться только пакетами, для которых он указан в качестве явной зависимости в файле манифеста: пакет Pub и один из пакетов Priv.

Карта путей среды проекта извлекается из файла манифеста. Путь к пакету uuid с именем X определяется следующими правилами (в порядке их приоритета):

  1. Если файл проекта в каталоге соответствует uuid и имени X, то либо:
  • У него есть запись path верхнего уровня, тогда uuid будет отображаться на этот путь, интерпретируемый относительно каталога, содержащего файл проекта.
  • В противном случае uuid отображается на src/X.jl относительно каталога, содержащего файл проекта.
  1. Если вышеупомянутое не выполняется, и файл проекта имеет соответствующий файл манифеста, и манифест содержит строку, соответствующую uuid , то:
  • Если у него есть запись path , использовать этот путь (относительно каталога, содержащего файл манифеста).
  • Если у него есть запись git-tree-sha1 , вычислить детерминированную функцию хеширования uuid и git-tree-sha1 — назовём её slug — и искать каталог под именем packages/X/$slug в каждом каталоге в глобальном массиве Julia DEPOT_PATH. Использовать первый такой каталог, который существует.

Если любой из этих результатов приводит к успеху, путь к точке входа исходного кода будет либо этим результатом, либо относительным путём от этого результата плюс src/X.jl; в противном случае, для uuid нет сопоставления путей. При загрузке X, если путь к исходному коду не найден, поиск завершится неудачей, и пользователю может быть предложено установить соответствующую версию пакета или принять другие корректирующие действия (например, объявление X в качестве зависимости).

В примере файла манифеста выше, чтобы найти путь к первому пакету Priv — с UUID ba13f791-ae1d-465a-978b-69c3ad90f72b — Julia ищет его строку в файле манифеста, видит, что у него есть запись path , смотрит на deps/Priv относительно каталога проекта App — предположим, что код App находится в /home/me/projects/App — видит, что /home/me/projects/App/deps/Priv существует и, следовательно, загружает Priv оттуда.

Если, с другой стороны, Julia загружала другой пакет Priv — тот, с UUID 2d15fe94-a1f7-436c-a4d8-07a9a496e01c — она находит его строку в манифесте, видит, что у него нет записи path, но есть запись git-tree-sha1 . Затем она вычисляет slug для этой пары UUID/SHA-1, которая равна HDkrT (точные детали этого вычисления не важны, но оно является согласованным и детерминированным). Это означает, что путь к этому пакету Priv будет packages/Priv/HDkrT/src/Priv.jl в одном из хранилищ пакетов. Предположим, что содержимое DEPOT_PATH равно ["/home/me/.julia", "/usr/local/julia"], тогда Julia проверит следующие пути, чтобы увидеть, существуют ли они:

  1. /home/me/.julia/packages/Priv/HDkrT
  2. /usr/local/julia/packages/Priv/HDkrT

Julia использует первый из них, который существует, чтобы попробовать загрузить общедоступный пакет Priv из файла packages/Priv/HDKrT/src/Priv.jl в хранилище, где он был найден.

Вот представление возможной карты путей для нашего примера проекта App среды, предоставленной в манифесте для графа зависимостей, после поиска в локальной файловой системе:

paths = Dict(
    # Priv – the private one:
    (UUID("ba13f791-ae1d-465a-978b-69c3ad90f72b"), :Priv) =>
        # relative entry-point inside `App` repo:
        "/home/me/projects/App/deps/Priv/src/Priv.jl",
    # Priv – the public one:
    (UUID("2d15fe94-a1f7-436c-a4d8-07a9a496e01c"), :Priv) =>
        # package installed in the system depot:
        "/usr/local/julia/packages/Priv/HDkr/src/Priv.jl",
    # Pub:
    (UUID("c07ecb7d-0dc9-4db7-8803-fadaaeaf08e1"), :Pub) =>
        # package installed in the user depot:
        "/home/me/.julia/packages/Pub/oKpw/src/Pub.jl",
    # Zebra:
    (UUID("f7a24cb4-21fc-4002-ac70-f0e3a0dd3f62"), :Zebra) =>
        # package installed in the system depot:
        "/usr/local/julia/packages/Zebra/me9k/src/Zebra.jl",
)

Эта примерная карта включает три разных типа местоположений пакетов (первый и третий являются частью стандартного пути загрузки):

  1. Частный пакет Priv вложен в хранилище App.
  2. Общедоступные пакеты Priv и Zebra находятся в системном хранилище, где хранятся пакеты, установленные и управляемые системным администратором. Они доступны всем пользователям системы.
  3. Пакет Pub находится в пользовательском хранилище, где хранятся пакеты, установленные пользователем. Они доступны только пользователю, который их установил.

Каталоги пакетов

Папки пакетов предоставляют более простой тип среды без возможности обработки столкновений имён. В папке пакетов набор основных пакетов — это набор подпапок, которые «выглядят как» пакеты. Пакет X существует в папке пакетов, если папка содержит один из следующих файлов «точки входа»:

  • X.jl
  • X/src/X.jl
  • X.jl/src/X.jl

От того, содержит ли пакет в папке пакетов файл проекта, зависит, какие зависимости он может импортировать:

  • Если у него есть файл проекта, он может импортировать только те пакеты, которые указаны в разделе [deps] файла проекта.
  • Если у него нет файла проекта, он может импортировать любой основной пакет — то есть те же пакеты, которые можно загрузить в Main или в интерактивную оболочку (REPL).

Карта корней определяется путём проверки содержимого папки пакетов для генерации списка всех существующих пакетов. Кроме того, каждому элементу будет присвоен UUID следующим образом: для данного пакета, найденного внутри папки X...

  1. Если X/Project.toml существует и имеет запись uuid, то uuid — это это значение.
  2. Если X/Project.toml существует, но не имеет записи основного UUID, то uuid — это фиктивный UUID, сгенерированный путём хэширования канонического (действительного) пути к X/Project.toml.
  3. В противном случае (если Project.toml не существует), то uuid — это нулевой UUID nil UUID.

Граф зависимостей каталога проекта определяется наличием и содержимым файлов проектов в подкаталоге каждого пакета. Правила следующие:

  • Если подкаталог пакета не содержит файла проекта, то он исключается из графа, а инструкции импорта в его коде рассматриваются как основные, такие же, как основной проект и интерактивная оболочка (REPL).
  • Если подкаталог пакета содержит файл проекта, то запись в графе для его UUID — это карта [deps] файла проекта, которая считается пустой, если раздел отсутствует.

В качестве примера предположим, что папка пакетов имеет следующую структуру и содержимое:

Aardvark/
    src/Aardvark.jl:
        import Bobcat
        import Cobra

Bobcat/
    Project.toml:
        [deps]
        Cobra = "4725e24d-f727-424b-bca0-c4307a3456fa"
        Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/Bobcat.jl:
        import Cobra
        import Dingo

Cobra/
    Project.toml:
        uuid = "4725e24d-f727-424b-bca0-c4307a3456fa"
        [deps]
        Dingo = "7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/Cobra.jl:
        import Dingo

Dingo/
    Project.toml:
        uuid = "7a7925be-828c-4418-bbeb-bac8dfc843bc"

    src/Dingo.jl:
        # no imports

Вот соответствующая структура корней, представленная в виде словаря:

roots = Dict(
    :Aardvark => UUID("00000000-0000-0000-0000-000000000000"), # no project file, nil UUID
    :Bobcat   => UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), # dummy UUID based on path
    :Cobra    => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), # UUID from project file
    :Dingo    => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), # UUID from project file
)

Вот соответствующая структура графа, представленная в виде словаря:

graph = Dict(
    # Bobcat:
    UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf") => Dict(
        :Cobra => UUID("4725e24d-f727-424b-bca0-c4307a3456fa"),
        :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
    ),
    # Cobra:
    UUID("4725e24d-f727-424b-bca0-c4307a3456fa") => Dict(
        :Dingo => UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"),
    ),
    # Dingo:
    UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc") => Dict(),
)

Несколько общих правил:

  1. Пакет без файла проекта может зависеть от любой основной зависимости, и поскольку каждый пакет в папке пакетов доступен на верхнем уровне, он может импортировать все пакеты в среде.
  2. Пакет с файлом проекта не может зависеть от пакета без файла проекта, так как пакеты с файлами проектов могут загружать только пакеты из graph, а пакеты без файлов проекта не отображаются в graph.
  3. Пакет с файлом проекта, но без явного UUID, может зависеть только от пакетов без файлов проектов, так как фиктивные UUID, присваиваемые этим пакетам, строго внутренние.

Обратите внимание на следующие конкретные примеры этих правил в нашем примере:

  • Aardvark может импортировать любой из Bobcat, Cobra или Dingo; он импортирует Bobcat и Cobra.
  • Bobcat может и импортирует оба Cobra и Dingo, у которых оба есть файлы проекта с UUID и объявлены как зависимости в разделе Bobcat's [deps].
  • Bobcat не может зависеть от Aardvark, так как Aardvark не имеет файла проекта.
  • Cobra может и импортирует Dingo, у которого есть файл проекта и UUID, и он объявлен как зависимость в разделе Cobra's [deps].
  • Cobra не может зависеть от Aardvark или Bobcat, так как ни у того, ни у другого нет реальных UUID.
  • Dingo не может импортировать ничего, потому что у него есть файл проекта без раздела [deps].

Карта путей в папке пакетов простая: она отображает имена подкаталогов в соответствующие пути к файлам точек входа. Другими словами, если путь к каталогу нашего примера проекта — /home/me/animals, то карта paths может быть представлена ​​этим словарем:

paths = Dict(
    (UUID("00000000-0000-0000-0000-000000000000"), :Aardvark) =>
        "/home/me/AnimalPackages/Aardvark/src/Aardvark.jl",
    (UUID("85ad11c7-31f6-5d08-84db-0a4914d4cadf"), :Bobcat) =>
        "/home/me/AnimalPackages/Bobcat/src/Bobcat.jl",
    (UUID("4725e24d-f727-424b-bca0-c4307a3456fa"), :Cobra) =>
        "/home/me/AnimalPackages/Cobra/src/Cobra.jl",
    (UUID("7a7925be-828c-4418-bbeb-bac8dfc843bc"), :Dingo) =>
        "/home/me/AnimalPackages/Dingo/src/Dingo.jl",
)

Поскольку все пакеты в среде папки пакетов, по определению, являются подкаталогами с ожидаемыми файлами точек входа, их записи в карте paths всегда имеют этот вид.

Стек сред

Третий и последний тип среды — это среда, которая объединяет другие среды путём наложения нескольких из них, делая пакеты в каждой доступными в единой составной среде. Эти составные среды называются стеками сред. Глобальная переменная Julia LOAD_PATH определяет стек сред — среду, в которой работает процесс Julia. Если вы хотите, чтобы ваш процесс Julia имел доступ только к пакетам в одном проекте или папке пакетов, сделайте его единственным элементом LOAD_PATH. Однако часто бывает полезно иметь доступ к некоторым из ваших любимых инструментов — стандартным библиотекам, профилировщикам, отладчикам, личным утилитам и т. д. — даже если они не являются зависимостями проекта, над которым вы работаете. Добавив среду, содержащую эти инструменты, в путь загрузки, вы сразу получите к ним доступ в коде верхнего уровня, не добавляя их в свой проект.

Механизм объединения структур данных корней, графов и путей компонентов стека сред прост: они объединяются как словари, отдавая приоритет более ранним записям по сравнению с более поздними в случае столкновений ключей. Другими словами, если у нас есть stack = [env₁, env₂, …] то у нас есть:

roots = reduce(merge, reverse([roots₁, roots₂, …]))
graph = reduce(merge, reverse([graph₁, graph₂, …]))
paths = reduce(merge, reverse([paths₁, paths₂, …]))

Подписанные переменные rootsᵢ, graphᵢ и pathsᵢ соответствуют подписанным средам, envᵢ, содержащимся в stack. reverse присутствует, потому что merge отдаёт приоритет последнему аргументу, а не первому, когда возникают коллизии между ключами в аргументных словарях. В этом дизайне есть несколько примечательных особенностей:

  1. Основная среда — то есть первая среда в стеке — надёжно встроена в среду стека. Полный граф зависимостей первой среды в стеке гарантированно включается в составную среду, включая те же версии всех зависимостей.
  2. Пакеты в средах, не являющихся основными, могут использовать несовместимые версии своих зависимостей, даже если их собственные среды полностью совместимы. Это может произойти, когда одна из их зависимостей затенена версией в более ранней среде в стеке (либо по графу, либо по пути, либо по обоим).

Поскольку основная среда обычно является средой проекта, над которым вы работаете, а среды, расположенные позже в стеке, содержат дополнительные инструменты, это правильный компромисс: лучше сломать инструменты разработки, но сохранить работу над проектом. В случае таких несовместимостей вам обычно придётся обновить инструменты разработки до версий, совместимых с основным проектом.

Заключение

Федеративное управление пакетами и точная воспроизводимость программного обеспечения являются сложными, но достойными целями в системе пакетов. В совокупности эти цели приводят к более сложному механизму загрузки пакетов, чем у большинства динамических языков, но также обеспечивают масштабируемость и воспроизводимость, которые чаще ассоциируются со статическими языками. Обычно пользователи Julia должны иметь возможность использовать встроенный менеджер пакетов для управления своими проектами, не нуждаясь в точном понимании этих взаимодействий. Вызов Pkg.add("X") добавит в соответствующие файлы проекта и манифеста, выбранные с помощью Pkg.activate("Y"), так что будущий вызов import X загрузит X без дальнейших раздумий.

© 2009–2019 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License.
https://docs.julialang.org/en/v1.1.1/manual/code-loading/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API