Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public keyword #50105

Merged
merged 69 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from 68 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
5b3d8c1
mwe including `export (scoped-true), a, b, c` and `names`
Jun 7, 2023
665855e
add `Base.isinternal` and use it to flag internal methods in their do…
Jun 7, 2023
52f519e
update documentation
Jun 7, 2023
03f9e02
add more verbose clarificaiton of what is and is not public API (TODO…
Jun 26, 2023
7eb64a2
add prelimianry list of scoped-exported symbols for review
Jun 26, 2023
c7192ea
more docs (implement option 1 of https://github.com/JuliaLang/julia/p…
Jun 26, 2023
ec3b2f3
address @jariji's comments
Jun 26, 2023
804f5ef
Merge branch 'master' into scoped-export
LilithHafner Jun 29, 2023
7d404f0
Merge branch 'master' into scoped-export
Jul 8, 2023
4dc10c4
use JuliaSyntax#320 branch
Jul 8, 2023
5fd1f64
bump JuliaSyntax
Jul 8, 2023
b85ae46
more changes for scoped export -> public
Jul 8, 2023
acc928a
try to make public list functional
Jul 10, 2023
a5162c2
bump juliasyntax
Jul 18, 2023
aacc187
bump JuliaSyntax
Jul 19, 2023
145ad7f
fix lowering
Jul 19, 2023
aea3d22
clarify internal symbol warning and add test for it
Jul 19, 2023
f7d57ba
add test for names + public
Jul 19, 2023
34844bb
don't autocomplete internals (ideally we _would_ autocomplete interna…
Jul 19, 2023
8241760
Merge branch 'master' into scoped-export
Jul 19, 2023
920244a
update FAQ (this change introduces comedy and therefore may be contro…
Jul 19, 2023
9e61f96
Rename isinternal to ispublic and make all resolved symbols in Main p…
Jul 19, 2023
f75cb68
fix typo in test/reflection.jl
Jul 21, 2023
db7ba05
change module docstring warning
Jul 28, 2023
7dbdd5e
Make warning an Admonition
Jul 28, 2023
ac11ea4
bump JuliaSyntax
Jul 28, 2023
bd0ee87
fix doc tests
Jul 29, 2023
1dc9652
remove internal warning tests from stdlib/REPL/test/docview.jl becaus…
Jul 29, 2023
17b8834
restore autocomplete for non-public symbols
Jul 29, 2023
6e2ddec
Merge branch 'master' into scoped-export
LilithHafner Jul 29, 2023
1000988
initialize public to false (woops!)
Jul 29, 2023
c5955c0
more fixups for doc tests
Jul 29, 2023
5ed2c7d
remove joke
Jul 29, 2023
5da758e
fix repl tests
Jul 29, 2023
de8be2b
fix precompile tests
Jul 29, 2023
d75725b
bump juliasyntax to merged version
Jul 30, 2023
05352df
Make `compilecache` internal
LilithHafner Aug 3, 2023
4b04f31
Remove `_datatype_*` publics
LilithHafner Aug 3, 2023
051f824
Remove symbols from public list
LilithHafner Aug 3, 2023
8a56ab4
Add some modules to public list (@vtjnash)
LilithHafner Aug 3, 2023
6e2d809
remove newlines between bullet points
Aug 10, 2023
69cf8d4
Apply suggestions from code review
LilithHafner Aug 13, 2023
9d74beb
Apply suggestions from code review
LilithHafner Aug 13, 2023
4b18e1d
Merge branch 'master' into scoped-export
Aug 13, 2023
4ba71ab
add show tests
Aug 13, 2023
ce3a798
group public symbols a little better
Aug 13, 2023
ca8eb44
revert names to the most basic implementation (simply swap export=>pu…
Aug 14, 2023
6d1e2c7
fix typo
Aug 14, 2023
2678523
fix typo
Aug 14, 2023
528469b
revert unnecessary whitespace change
Aug 14, 2023
082fb86
add tests for ispublic and add more tests for isexported
Aug 14, 2023
3d09dae
fix some tests
Aug 14, 2023
12bab72
Do a better job of logging nonpublic access in help mode
Sep 4, 2023
0112e2c
remove obsolete tests
Sep 4, 2023
66427c6
finalize struct
Sep 4, 2023
067d69c
update tests
Sep 4, 2023
b688b60
add tests and fix "public names" header
Sep 4, 2023
6ce6fdb
update documentation throughout to refer to public and export now-cor…
Sep 5, 2023
e57c63f
fix cross refs
Sep 5, 2023
1a6e3dc
update varinfo documentation
Sep 5, 2023
ebcdf24
Merge branch 'master' into scoped-export
LilithHafner Sep 5, 2023
d4418fe
Update checksums
Sep 5, 2023
c4924c9
fix some tests
Sep 5, 2023
5835699
more test fixes
Sep 5, 2023
153b538
try switching to a different JuliaSyntax version
Sep 5, 2023
158d59f
don't accidentally drop the module in testing
Sep 5, 2023
41f3729
fix some doctests
Sep 5, 2023
fd5fb90
Merge branch 'master' into scoped-export
LilithHafner Sep 6, 2023
ea7a141
Docstring fixup for Base.ispublic
LilithHafner Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion base/docs/basedocs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,23 @@ kw"import"
"""
export

`export` is used within modules to tell Julia which functions should be
`export` is used within modules to tell Julia which names should be
made available to the user. For example: `export foo` makes the name
`foo` available when [`using`](@ref) the module.
See the [manual section about modules](@ref modules) for details.
"""
kw"export"

"""
public

`public` is used within modules to tell Julia which names are part of the
public API of the module . For example: `public foo` indicates that the name
`foo` is public, without making it available available when [`using`](@ref)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo ("available available")?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the module. See the [manual section about modules](@ref modules) for details.
"""
kw"public"

"""
as

Expand Down
81 changes: 81 additions & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1063,3 +1063,84 @@ export
@view,
@views,
@static

# TODO: use normal syntax once JuliaSyntax.jl becomes available at this point in bootstrapping
eval(Expr(:public,
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
# Modules
:Checked,
:Filesystem,
:Order,
:Sort,

# Types
:AbstractLock,
:AsyncCondition,
:CodeUnits,
:Event,
:Fix1,
:Fix2,
:Generator,
:ImmutableDict,
:OneTo,
:UUID,

# Semaphores
:Semaphore,
:acquire,
:release,

# collections
:IteratorEltype,
:IteratorSize,
:to_index,
:vect,
:isdone,
:front,
:rest,
:split_rest,
:tail,
:checked_length,

# Loading
:DL_LOAD_PATH,
:load_path,
:active_project,

# Reflection and introspection
:isambiguous,
:isexpr,
:isidentifier,
:issingletontype,
:identify_package,
:locate_package,
:moduleroot,
:jit_total_bytes,
:summarysize,
:isexported,
:ispublic,

# Opperators
:operator_associativity,
:operator_precedence,
:isbinaryoperator,
:isoperator,
:isunaryoperator,

# C interface
:cconvert,
:unsafe_convert,

# Error handling
:exit_on_sigint,
:windowserror,

# Macros
Symbol("@assume_effects"),
Symbol("@constprop"),
Symbol("@locals"),
Symbol("@propagate_inbounds"),

# misc
:notnothing,
:runtests,
:text_colors))
86 changes: 81 additions & 5 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,100 @@ end
"""
names(x::Module; all::Bool = false, imported::Bool = false)

Get an array of the names exported by a `Module`, excluding deprecated names.
If `all` is true, then the list also includes non-exported names defined in the module,
Get an array of the public names of a `Module`, excluding deprecated names.
If `all` is true, then the list also includes non-public names defined in the module,
deprecated names, and compiler-generated names.
If `imported` is true, then names explicitly imported from other modules
are also included.

As a special case, all names defined in `Main` are considered \"exported\",
since it is not idiomatic to explicitly export names from `Main`.
As a special case, all names defined in `Main` are considered \"public\",
since it is not idiomatic to explicitly mark names from `Main` as public.

See also: [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref).
See also: [`isexported`](@ref), [`ispublic`](@ref), [`@locals`](@ref Base.@locals), [`@__MODULE__`](@ref).
"""
names(m::Module; all::Bool = false, imported::Bool = false) =
sort!(unsorted_names(m; all, imported))
unsorted_names(m::Module; all::Bool = false, imported::Bool = false) =
ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint), m, all, imported)

"""
isexported(m::Module, s::Symbol) -> Bool

Returns whether a symbol is exported from a module.

See also: [`ispublic`](@ref), [`names`](@ref)

```jldoctest
julia> module Mod
export foo
public bar
end
Mod

julia> Base.isexported(Mod, :foo)
true

julia> Base.isexported(Mod, :bar)
false

julia> Base.isexported(Mod, :baz)
false
```
"""
isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0

"""
ispublic(m::Module, s::Symbol) -> Bool

Returns whether a symbol is marked as public in a module.

Exported symbols are considered public.

See also: [`isexported`](@ref), [`names`](@ref)

```jldoctest
julia> module Mod
export foo
public bar
end
Mod

julia> Base.isexported(Mod, :foo)
true

julia> Base.isexported(Mod, :bar)
false

julia> Base.isexported(Mod, :baz)
false
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
```
"""
ispublic(m::Module, s::Symbol) = ccall(:jl_module_public_p, Cint, (Any, Any), m, s) != 0

# TODO: this is vaguely broken because it only works for explicit calls to
# `Base.deprecate`, not the @deprecated macro:
isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0

"""
isbindingresolved(m::Module, s::Symbol) -> Bool

Returns whether the binding of a symbol in a module is resolved.

See also: [`isexported`](@ref), [`ispublic`](@ref), [`isdeprecated`](@ref)

```jldoctest
julia> module Mod
foo() = 17
end
Mod

julia> Base.isbindingresolved(Mod, :foo)
true

julia> Base.isbindingresolved(Mod, :bar)
false
```
"""
isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0

function binding_module(m::Module, s::Symbol)
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2226,7 +2226,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
print(io, head, ' ')
show_list(io, args, ", ", indent, 0, quote_level)

elseif head === :export
elseif head in (:export, :public)
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
print(io, head, ' ')
show_list(io, mapany(allow_macroname, args), ", ", indent)

Expand Down
2 changes: 1 addition & 1 deletion deps/JuliaSyntax.version
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
JULIASYNTAX_BRANCH = main
JULIASYNTAX_SHA1 = 045d156c44dbb87769c7416d049a7c08908539d4
JULIASYNTAX_SHA1 = a9110fa8ecbe79943bb9525b4ccd99a3976cfcb7
JULIASYNTAX_GIT_URL := https://github.com/JuliaLang/JuliaSyntax.jl.git
JULIASYNTAX_TAR_URL = https://api.github.com/repos/JuliaLang/JuliaSyntax.jl/tarball/$1

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a42bbb42babbbd727556f6bc01455826
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
d542766e72b57418b9b4e17743f89d8535c1f36497346b57538bd0cb451e64af9493015692179f80ec4ee8cf18349c1e0888f5710db895e19f9bb0322f0f7464
4 changes: 4 additions & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ However, you can create variables with names:
Finally:
`where` is parsed as an infix operator for writing parametric method and type definitions;
`in` and `isa` are parsed as infix operators;
`public` is parsed as a keyword when beginning a toplevel statement;
`outer` is parsed as a keyword when used to modify the scope of a variable in an iteration specification of a `for` loop;
and `as` is used as a keyword to rename an identifier brought into scope by `import` or `using`.
Creation of variables named `where`, `in`, `isa`, `outer` and `as` is allowed, though.

```@docs
module
export
public
import
using
as
Expand Down Expand Up @@ -451,6 +453,8 @@ Base.@__DIR__
Base.@__LINE__
Base.fullname
Base.names
Base.isexported
Base.ispublic
Base.nameof(::Function)
Base.functionloc(::Any, ::Any)
Base.functionloc(::Method)
Expand Down
6 changes: 3 additions & 3 deletions doc/src/base/reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ Julia provides a variety of runtime reflection capabilities.

## Module bindings

The exported names for a `Module` are available using [`names(m::Module)`](@ref), which will return
an array of [`Symbol`](@ref) elements representing the exported bindings. `names(m::Module, all = true)`
returns symbols for all bindings in `m`, regardless of export status.
The public names for a `Module` are available using [`names(m::Module)`](@ref), which will return
an array of [`Symbol`](@ref) elements representing the public bindings. `names(m::Module, all = true)`
returns symbols for all bindings in `m`, regardless of public status.

## DataType fields

Expand Down
27 changes: 22 additions & 5 deletions doc/src/manual/faq.md
LilithHafner marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,28 @@ On the other hand, language *interoperability* is extremely useful: we want to e

### How does Julia define its public API?

Julia `Base` and standard library functionality described in the
[the documentation](https://docs.julialang.org/) that is not marked as unstable
(e.g. experimental and internal) is covered by [SemVer](https://semver.org/).
Functions, types, and constants are not part of the public API if they are not
included in the documentation, _even if they have docstrings_.
Julia's public [API](https://en.wikipedia.org/wiki/API) is the behavior described in
documentation of public symbols from `Base` and the standard libraries. Functions,
types, and constants are not part of the public API if they are not public, even if
they have docstrings or are described in the documentation. Further, only the documented
behavior of public symbols is part of the public API. Undocumented behavior of public
symbols is internal.
Comment on lines +25 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still some symbols which are documented in the manual and not yet marked as public (e.g. Base.EnvDict). Is there a plan to try to find them all? otherwise should we also keep around for the time being the old version mentioning the documentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sorta responded to this way up the thread: #50105 (comment)

The plan is to find them all by 1.11. #51335.


Public symbols are those marked with either `public foo` or `export foo`.

In other words:

- Documented behavior of public symbols is part of the public API.
- Undocumented behavior of public symbols is not part of the public API.
- Documented behavior of private symbols is not part of the public API.
- Undocumented behavior of private symbols is not part of the public API.

You can get a complete list of the public symbols from a module with `names(MyModule)`.

Package authors are encouraged to define their public API similarly.

Anything in Julia's Public API is covered by [SemVer](https://semver.org/) and therefore
will not be removed or receive meaningful breaking changes before Julia 2.0.

### There is a useful undocumented function/type/constant. Can I use it?

Expand Down
9 changes: 7 additions & 2 deletions doc/src/manual/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Modules in Julia help organize code into coherent units. They are delimited synt
allows the same name to be used for different functions or global variables without conflict, as long as they are in separate modules.

2. Modules have facilities for detailed namespace management: each defines a set of names it
`export`s, and can import names from other modules with `using` and `import` (we explain these below).
`export`s and marks as `public`, and can import names from other modules with `using` and
`import` (we explain these below).

3. Modules can be precompiled for faster loading, and may contain code for runtime initialization.

Expand All @@ -16,7 +17,7 @@ Typically, in larger Julia packages you will see module code organized into file
```julia
module SomeModule

# export, using, import statements are usually here; we discuss these below
# export, public, using, import statements are usually here; we discuss these below

include("file1.jl")
include("file2.jl")
Expand Down Expand Up @@ -103,6 +104,10 @@ Also, some modules don't export names at all. This is usually done if they use c
words, such as `derivative`, in their API, which could easily clash with the export lists of other
modules. We will see how to manage name clashes below.

To mark a name as public without exporting it into the namespace of folks who call `using NiceStuff`,
one can use `public` instead of `export`. This marks the public name(s) as part of the public API,
but does not have any namespace implications.

### Standalone `using` and `import`

Possibly the most common way of loading a module is `using ModuleName`. This [loads](@ref
Expand Down
7 changes: 4 additions & 3 deletions doc/src/manual/noteworthy-differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,15 +413,16 @@ For users coming to Julia from R, these are some noteworthy differences:
file are `include`d only once (No `#ifdef` confusion).

### Julia ⇔ C/C++: Module interface
* C++ exposes interfaces using "public" `.h`/`.hpp` files whereas Julia `module`s `export`
symbols that are intended for their users.
* C++ exposes interfaces using "public" `.h`/`.hpp` files whereas Julia `module`s mark
specific symbols that are intended for their users as `public`or `export`ed.
* Often, Julia `module`s simply add functionality by generating new "methods" to existing
functions (ex: `Base.push!`).
* Developers of Julia packages therefore cannot rely on header files for interface
documentation.
* Interfaces for Julia packages are typically described using docstrings, README.md,
static web pages, ...
* Some developers choose not to `export` all symbols required to use their package/module.
* Some developers choose not to `export` all symbols required to use their package/module,
but should still mark unexported user facing symbols as `public`.
* Users might be expected to access these components by qualifying functions/structs/...
with the package/module name (ex: `MyModule.run_this_task(...)`).

Expand Down
2 changes: 2 additions & 0 deletions src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ JL_DLLEXPORT jl_sym_t *jl_top_sym;
JL_DLLEXPORT jl_sym_t *jl_module_sym;
JL_DLLEXPORT jl_sym_t *jl_slot_sym;
JL_DLLEXPORT jl_sym_t *jl_export_sym;
JL_DLLEXPORT jl_sym_t *jl_public_sym;
JL_DLLEXPORT jl_sym_t *jl_import_sym;
JL_DLLEXPORT jl_sym_t *jl_toplevel_sym;
JL_DLLEXPORT jl_sym_t *jl_quote_sym;
Expand Down Expand Up @@ -304,6 +305,7 @@ void jl_init_common_symbols(void)
jl_lambda_sym = jl_symbol("lambda");
jl_module_sym = jl_symbol("module");
jl_export_sym = jl_symbol("export");
jl_public_sym = jl_symbol("public");
jl_import_sym = jl_symbol("import");
jl_using_sym = jl_symbol("using");
jl_assign_sym = jl_symbol("=");
Expand Down
2 changes: 1 addition & 1 deletion src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,7 @@ static void add_intrinsic(jl_module_t *inm, const char *name, enum intrinsic f)
jl_value_t *i = jl_permbox32(jl_intrinsic_type, 0, (int32_t)f);
jl_sym_t *sym = jl_symbol(name);
jl_set_const(inm, sym, i);
jl_module_export(inm, sym);
jl_module_public(inm, sym, 1);
}

void jl_init_intrinsic_properties(void) JL_GC_DISABLED
Expand Down
Loading