Changelogs » Abjad

Abjad

3.0.0

NEW CONFIGURATION FUNCTIONALITY:

* Added AbjadConfiguration.composer_scores_directory

* Added composer template data to AbjadConfiguration

* Integrated pathlib into abjad.Configuration

The four main abjad.Configuration properties now return path objects
instead of strings:

* abjad.Configuration.configuration_directory
* abjad.Configuration.configuration_file_path
* abjad.Configuration.home_directory
* abjad.Configuration.temp_directory

* Changed 'directory_path' to just 'directory' in abjad.Configuration

OLD: Configuration.configuration_file_name
NEW: Configuration.file_path

OLD: Configuration.configuration_directory_name
NEW: Configuration.configuration_directory

NEW CONTAINER FUNCTIONALITY:

* Added bracket comments to measure-open, measure-close.

OLD:

>>> measure = abjad.Measure((3, 4), "c'4 d' e'")
>>> abjad.f(measure)
{
\time 3/4
c'4
d'4
e'4
}

NEW:

>>> measure = abjad.Measure((3, 4), "c'4 d' e'")
>>> abjad.f(measure)
{ % measure
\time 3/4
c'4
d'4
e'4
} % measure

* Removed Container.reverse(); no longer supported

* Removed Component.name; preserved Container.name

* Taught containers to initialize from collections of strings:

staff = abjad.Staff([
r"\times 9/10 { r8 c'16 c'16 bf'4 ~ bf'16 r16 }",
r"\times 9/10 { bf'16 e''16 e''4 ~ e''16 r16 fs''16 af''16 }",
r"\times 4/5 { a'16 r4 }",
])

abjad.f(staff)
\new Staff {
{
\tweak text tuplet-number::calc-fraction-text
\times 9/10 {
r8
c'16
c'16
bf'4 ~
bf'16
r16
}
}
{
\tweak text tuplet-number::calc-fraction-text
\times 9/10 {
bf'16
e''16
e''4 ~
e''16
r16
fs''16
af''16
}
}
{
\times 4/5 {
a'16
r4
}
}
}

* Taught simultaneous containers to accept vanilla containers
(in addition to contexts)

OLD: abjad.Container.music
NEW: abjad.Container.components

NEW CONTEXT FUNCTIONALITY:

OLD: abjad.Context.context_name
NEW: abjad.Context.lilypond_type

* Closes 895

NEW DATA STRUCTURE FUNCTIONALITY:

* Removed abjad.ClefList; use list instead
* Removed abjad.PerformerList; use list instead
* Removed abjad.PitchRangeList; use list instead

* Removed abjad.InstrumentDictionary; use abjad.OrderedDict instead
* Removed abjad.MetronomeMarkDictionary; use abjad.OrderedDict instead

OLD: abjad.TypedOrderedDict
NEW: abjad.OrderedDict

* Closes 909

NEW FORMAT FUNCTIONALITY:

* Added abjad.Container.identifier property. Use to set parse handles on any
Abjad container:

>>> container = abjad.Container("c'4 d' e' f'")
>>> container.identifier = '%*% AB'

>>> abjad.f(container)
{   %*% AB
c'4
d'4
e'4
f'4
}   %*% AB

* Changed context open-statement formatting to strictly one-per-line:

OLD:

\new Score \with {
\override BarLine.stencil = f
\override BarNumber.transparent = t
\override SpanBar.stencil = f
\override TimeSignature.stencil = f
} <<
\new PianoStaff <<
\context Staff = "Treble Staff" {
\clef "treble"
r16
r16
r16
r16
r16
c'16
d'16
e'16
f'16
g'16
}
\context Staff = "Bass Staff" {
\clef "bass"
c16
d16
e16
f16
g16
r16
r16
r16
r16
r16
}
>>
>>

NEW:

\new Score
\with
{
\override BarLine.stencil = f
\override BarNumber.transparent = t
\override SpanBar.stencil = f
\override TimeSignature.stencil = f
}
<<
\new PianoStaff
<<
\context Staff = "Treble Staff"
{
\clef "treble"
r16
r16
r16
r16
r16
c'16
d'16
e'16
f'16
g'16
}
\context Staff = "Bass Staff"
{
\clef "bass"
c16
d16
e16
f16
g16
r16
r16
r16
r16
r16
}
>>
>>

MODEL. Note that this makes lexical postprocessing incredibly easy. Any chunk
of anything can be parsed out of Abjad's LilyPond output as a sequence of
consecutive lines. Target application might be anything from assignment to
variables (for externalization in complex scores) or tagging parts of LilyPond
files on and off.

* Removed 'right' format slot

NEW INDICATOR FUNCTIONALITY:

* Added abjad.hairpin() factory function:

With three-part string descriptor:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> abjad.hairpin('p < f', staff[:])
>>> abjad.override(staff[0]).dynamic_line_spanner.staff_padding = 4

>>> abjad.f(staff)
\new Staff
{
\once \override DynamicLineSpanner.staff-padding = 4
c'4
\p
\<
d'4
e'4
f'4
\f
}

With dynamic objects:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> start = abjad.Dynamic('niente', command=r'\!')
>>> trend = abjad.DynamicTrend('o<')
>>> abjad.tweak(trend).color = 'blue'
>>> stop = abjad.Dynamic('"f"')
>>> abjad.hairpin([start, trend, stop], staff[:])
>>> abjad.override(staff[0]).dynamic_line_spanner.staff_padding = 4

>>> abjad.f(staff)
\new Staff
{
\once \override DynamicLineSpanner.staff-padding = 4
c'4
\!
- \tweak color blue
- \tweak circled-tip t
- \tweak stencil abjad-flared-hairpin
\<
d'4
e'4
f'4
_ (make-dynamic-script
(markup
:whiteout
:line (
:general-align Y -2 :normal-text :larger "“"
:hspace -0.4
:dynamic "f"
:hspace -0.2
:general-align Y -2 :normal-text :larger "”"
)
)
)
}

* Closes 991

* Added abjad.HairpinStart:

>>> staff = abjad.Staff("c'4 d' e' f' c' d' e' r4")
>>> abjad.attach(abjad.Dynamic('p'), staff[0])
>>> abjad.attach(abjad.HairpinStart('<|'), staff[0])
>>> abjad.attach(abjad.Dynamic('f'), staff[3])
>>> abjad.attach(abjad.Dynamic('f'), staff[4])
>>> abjad.attach(abjad.HairpinStart('|>o'), staff[4])
>>> abjad.attach(abjad.Dynamic('niente', command=r'\!'), staff[-1])
>>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4.5

>>> abjad.f(staff)
\new Staff
\with
{
\override DynamicLineSpanner.staff-padding = 4.5
}
{
c'4
\p
- \tweak stencil abjad-flared-hairpin
\<
d'4
e'4
f'4
\f
c'4
\f
- \tweak circled-tip t
- \tweak stencil abjad-flared-hairpin
\>
d'4
e'4
r4
\!
}

* Added abjad-flared-hairpin to docs/source/_stylesheets/default.ily

* Added abjad.text_spanner() factory function:

Single spanner:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> start_text_span = abjad.StartTextSpan(
...     left_text=abjad.Markup('pont.').upright(),
...     right_text=abjad.Markup('tasto').upright(),
...     style='solid_line_with_arrow',
...     )
>>> abjad.text_spanner(staff[:], start_text_span=start_text_span)
>>> abjad.override(staff[0]).text_spanner.staff_padding = 4

>>> abjad.f(staff)
\new Staff
{
\once \override TextSpanner.staff-padding = 4
c'4
- \abjad_solid_line_with_arrow
- \tweak bound-details.left.text \markup {
\concat
{
\upright
pont.
\hspace
0.5
}
}
- \tweak bound-details.right.text \markup {
\upright
tasto
}
\startTextSpan
d'4
e'4
f'4
\stopTextSpan
}

* Added abjad.StartTextSpan, abjad.StopTextSpan indicators

* Closes 989

* Added abjad.Dynamic.command property:

>>> dynamic = abjad.Dynamic('mf', command=r'\sub_mf')
>>> abjad.f(dynamic)
\sub_mf

Allows composers to define arbitrary dynamics (in external stylesheet) and
format with Abjad dynamic objects.

* Added abjad.Dynamic.name_is_textual property:

The property allows for ad hoc dynamic indications of the type like "barely
audible", "extremely quiet", "extremely loud", etc.

Textual dynamics format like this when initialized without an explicit
command:

>>> voice = abjad.Voice("c'4 d' e' f'")
>>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True)
>>> abjad.attach(dynamic, voice[0])

>>> abjad.f(voice)
\new Voice
{
c'4 _ (make-dynamic-script (markup :whiteout :normal-text :italic "appena udibile"))
d'4
e'4
f'4
}

Textual dynamics format like this when initialized with an explicit
command:

>>> voice = abjad.Voice("c'4 d' e' f'")
>>> dynamic = abjad.Dynamic(
...     'appena udibile',
...     command=r'\appena_udibile',
...     name_is_textual=True,
...     )
>>> abjad.attach(dynamic, voice[0])

>>> abjad.f(voice)
\new Voice
{
c'4 \appena_udibile
d'4
e'4
f'4
}

* Added abjad.MarginMarkup

* Added abjad.Momento for persistent indicator storage to disk

* Added abjad.RehearsalMark.from_string()

* Added abjad.TimeSignature.from_string() constructor

* Added abjad.MetricModulation.hide property

* Added new 'hide' property to three persistent indicators:

* abjad.Clef.hide
* abjad.Dynamic.hide
* abjad.MetronomeMark.hide

* Constrained MetronomeMark.units_per_minute to rationals

* Exteneded abjad.LilyPondLiteral to work with multiline input

* Fixed annotation / abjad.inspect().get_effective() bug

* Fixed abjad.mutate().wrap() / contexted indicator bug

* Set 'persistent' property on persistent indicators

* Closes 894

* Set 'redraw' class constant to true:

* abjad.Clef
* abjad.Instrument
* abjad.KeySignature

* Taught abjad.attach(..., context='CustomContext') to work with custom
context names

OLD: default_scope
NEW: context

OLD: abjad.attach(..., scope='Staff')
NEW: abjad.attach(..., context='Staff')

* Closes 873.

OLD: abjad.Dynamic.context == 'Staff'
NEW: abjad.Dynamic.context == 'Voice'

OLD: abjad.Instrument.short_name_markup
NEW: abjad.Instrument.short_markup

OLD: abjad.Instrument.name_markup
NEW: abjad.Instrument.markup

OLD: abjad.KeyCluster.suppress
NEW: abjad.KeyCluster.hide

OLD: abjad.LilyPondCommand is removed
NEW: use abjad.LilyPondLiteral instead

OLD: abjad.TimeSignature.suppress
NEW: abjad.TimeSignature.hide

* Removed abjad.SystemBreak; use abjad.LilyPondLiteral instead

* Removed abjad.Accelerando
* Removed abjad.Ritardando

* Removed ArrowLineSegment
* Removed LineSegment

NEW INSPECTION FUNCTIONALITY:

OLD: abjad.select().get_spanners()
NEW: abjad.inspect().get_spanners()

OLD: abjad.inspect().get_spanners() returned a SET
NEW: abjad.inspect().get_spanners() returns a LIST

OLD: abjad.select().get_duration()
NEW: abjad.inspect().get_duration()

OLD: abjad.select().get_pitches()
NEW: abjad.inspect().get_pitches()

OLD: abjad.select().get_timespan()
NEW: abjad.inspect().get_timespan()

OLD: abjad.inspect().tabulate_well_formedness_violations()
NEW: abjad.inspect().tabulate_wellformedness()

OLD:

* abjad.inspect().get_after_grace_container()
* abjad.inspect().get_annotation()
* abjad.inspect().get_badly_formed_components()
* abjad.inspect().get_contents()
* abjad.inspect().get_descendants()
* abjad.inspect().get_duration()
* abjad.inspect().get_effective()
* abjad.inspect().get_effective_staff()
* abjad.inspect().get_effective_wrapper()
* abjad.inspect().get_grace_container()
* abjad.inspect().get_indicator()
* abjad.inspect().get_indicators()
* abjad.inspect().get_leaf()
* abjad.inspect().get_lineage()
* abjad.inspect().get_logical_tie()
* abjad.inspect().get_markup()
* abjad.inspect().get_parentage()
* abjad.inspect().get_pitches()
* abjad.inspect().get_sounding_pitch()
* abjad.inspect().get_sounding_pitches()
* abjad.inspect().get_spanner()
* abjad.inspect().get_spanners()
* abjad.inspect().get_tuplet()
* abjad.inspect().get_vertical_moment()
* abjad.inspect().get_vertical_moment_at()

NEW:

* abjad.inspect().after_grace_container()
* abjad.inspect().annotation()
* abjad.inspect().badly_formed_components()
* abjad.inspect().contents()
* abjad.inspect().descendants()
* abjad.inspect().duration()
* abjad.inspect().effective()
* abjad.inspect().effective_staff()
* abjad.inspect().effective_wrapper()
* abjad.inspect().grace_container()
* abjad.inspect().indicator()
* abjad.inspect().indicators()
* abjad.inspect().leaf()
* abjad.inspect().lineage()
* abjad.inspect().logical_tie()
* abjad.inspect().markup()
* abjad.inspect().parentage()
* abjad.inspect().pitches()
* abjad.inspect().sounding_pitch()
* abjad.inspect().sounding_pitches()
* abjad.inspect().spanner()
* abjad.inspect().spanners()
* abjad.inspect().tuplet()
* abjad.inspect().vertical_moment()
* abjad.inspect().vertical_moment_at()

NEW INSTRUMENT FUNCTIONALITY:

* Abjad instruments no longer format LilyPond \instrumentName, \shortInstrumentName:

* Use abjad.StartMarkup to format LilyPond \instrumentName
* Use abjad.MarginMarkup to format LilyPond \shortInstrumentName

* Closes 464

OLD: abjad.Instrument.short_instrument_name
NEW: abjad.Instrument.short_name

OLD: abjad.Instrument.sounding_pitch_of_written_middle_c
NEW: abjad.Instrument.middle_c_sounding_pitch

NEW ITERATION FUNCTIONALITY:

OLD:

* abjad.iterate().by_prototype()
* abjad.iterate().by_leaf_pair()
* abjad.iterate().by_leaf()
* abjad.iterate().by_logical_tie()
* abjad.iterate().by_pitch()
* abjad.iterate().by_timeline()
* abjad.iterate().by_vertical_moment()

NEW:

* abjad.iterate().components()
* abjad.iterate().leaf_pairs()
* abjad.iterate().leaves()
* abjad.iterate().logical_ties()
* abjad.iterate().pitches()
* abjad.iterate().timeline()
* abjad.iterate().vertical_moments()

OLD: abjad.iterate().by_timeline_and_logical_tie()
NEW: use abjad.iterate().by_timeline() instead

OLD: abjad.iterate().by_timeline_from_component()
NEW: use abjad.iterate().timeline() instead

OLD: abjad.iterate().components(pitched=None) keyword
NEW: abjad.iterate().leaves(pitched=None) keyword

OLD: with_grace_notes=True
NEW: grace_notes=True

OLD: abjad.iterate().components(start=None, stop=None) keywords.
NEW: abjad.iterate().components()[:]

OLD: abjad.iterate().leaves(start=None, stop=None) keywords.
NEW: abjad.iterate().leaves()[:]

* Removed abjad.iterate().by_topmost_logical_ties_and_components().

Disambiguated meaning of 'nontrivial' keyword in logical tie iteration:

OLD:

* abjad.iterate().logical_ties(nontrivial=None):
yield all logical ties
* abjad.iterate().logical_ties(nontrivial=False):
yield all logical ties
* abjad.iterate().logical_ties(nontrivial=True):
yield only nontrivial logical ties

NEW:

* abjad.iterate().logical_ties(nontrivial=None):
yield all logical ties
* abjad.iterate().logical_ties(nontrivial=False):
yield only trivial logical ties
* abjad.iterate().logical_ties(nontrivial=True):
yield only nontrivial logical ties

Disambiguated meaning of abjad.iterate().leaves(grace_notes=None) keyword:

OLD:

* abjad.iterate().leaves(grace_notes=None):
yield big leaves only (ie, without grace notes)
* abjad.iterate().leaves(grace_notes=False):
same as above
* abjad.iterate().leaves(grace_notes=True):
yield both big leaves and grace leaves

NEW:

* abjad.iterate().leaves(grace_notes=None):
yield both big leaves and grace leaves
* abjad.iterate().leaves(grace_notes=False):
yield only big leaves (ie, without grace notes)
* abjad.iterate().leaves(grace_notes=True):
yield only grace leaves (ie, without big notes)

* Made abjad.iterate()._depth_first() private for developers

NEW LABEL FUNCTIONALITY:

* Added abjad.label().with_pitches(locale='us') keyword.

* Added abjad.label().with_start_offsets(global_offset=None) keyword.

* Taught abjad.label().with_start_offsets() to return total duration.

* Added abjad.Label.with_start_offsets(..., markup_command=None) keyword:

Provides a generalized hook for user-defined custom markup commands:

>>> staff = abjad.Staff(r"c'2 d' e' f'")
>>> score = abjad.Score([staff])
>>> mark = abjad.MetronomeMark((1, 4), 60)
>>> abjad.attach(mark, staff[0])
>>> abjad.label(staff).with_start_offsets(
...     clock_time=True,
...     markup_command='make-dark-cyan',
...     )

>>> abjad.f(score)
\new Score
<<
\new Staff
{
\tempo 4=60
c'2
^ \markup {
\make-dark-cyan
0'00''
}
d'2
^ \markup {
\make-dark-cyan
0'02''
}
e'2
^ \markup {
\make-dark-cyan
0'04''
}
f'2
^ \markup {
\make-dark-cyan
0'06''
}
}
>>

Note that the markup command make-dark-cyan must be user-defined and included
for LilyPond for interpret the output shown above. Example definition:

(define-markup-command (make-dark-cyan layout props text) (markup?)
"Dark cyan with font size 3."
(interpret-markup layout props
{\markup \fontsize 3 \with-color (x11-color 'DarkCyan) { text } }
)
)

NEW LILYPONDFILE FUNCTIONALITY:

OLD: abjad.LilyPondFile.new() included a date-time token by default.
NEW: abjad.LilyPondFile.new() no longer includes a date-time token by default. (Use abjad.LilyPondFile.new(..., date_time_token=True) for the old behavior.

The motivation is that the date-time comment included at the top of
most realworld files just leads to versioning contention under Git.
Easier to just exclude by default and then allow users to include in
the cases where they actually need it.

NEW MARKUP FUNCTIONALITY:

* Added abjad.Markup.abjad_metronome_mark()

* Added abjad.Markup.from_literal(). Enables markup initialization that
bypasses the parser.

* Added Markup.literal property:

>>> string = r'\custom-function 1 4'
>>> markup = abjad.Markup.from_literal(string, literal=True)

>>> abjad.f(markup)
\markup \custom-function 1 4

* Added abjad.Markup.with_literal() method:

>>> markup = abjad.Markup('Allegro assai')
>>> markup = markup.bold()
>>> markup = markup.with_literal(r'\user-markup-command')

>>> abjad.f(markup)
\markup {
\user-markup-command
\bold
"Allegro assai"
}

* Fixed longstanding abjad.MarkupCommand.__eq__() bug.

* Removed unused abjad.Markup.stack_priority property

* Removed markup courtesy autocolumn:

OLD:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> abjad.attach(abjad.Markup('Allegro'), staff[0])
>>> abjad.attach(abjad.Markup('non troppo'), staff[0])

>>> abjad.f(staff)
\new Staff
{
c'4
- \markup {
\column
{
\line
{
Allegro
}
\line
{
"non troppo"
}
}
}
d'4
e'4
f'4
}

NEW:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> abjad.attach(abjad.Markup('Allegro'), staff[0])
>>> abjad.attach(abjad.Markup('non troppo'), staff[0])

>>> abjad.f(staff)
\new Staff
{
c'4
- \markup { Allegro }
- \markup { "non troppo" }
d'4
e'4
f'4
}

* Closes 977

* Taught abjad.Markup.with_color() about abjad.SchemeColor.

NEW MATH FUNCTIONALITY:

* Added abjad.Ratio.reciprocal

NEW MUTATION FUNCTIONALITY:

* Fixed abjad.mutate().copy() bug from mutating dictionary while iterating

* Removed include_enclosing_containers=None keyword from
abjad.mutate().copy(); no longer supported

* Removed n=1 keyword from abjad.mutate().copy(); no longer supported

NEW NOTE-HEAD FUNCTIONALITY:

* Added abjad.NoteHead.alternative for edition-tagging. Set the
read / write NoteHead.alternative_property to a triple:

* alternative note-head
* (deactivated) other-edition tag
* active this-edition tag

>>> chord = abjad.Chord("<c' d' bf''>4")
>>> alternative = abjad.new(chord.note_heads[0])
>>> alternative.is_forced = True
>>> chord.note_heads[0].alternative = (alternative, '-PARTS', '+PARTS')

>>> abjad.f(chord, strict=50)
<
c'                                            %! +PARTS
%% c'!                                           %! -PARTS
d'
bf''
>4

The example above allows the C-natural to print with a forced natural sign
in the score (-PARTS) while printing normally (no forced accidental) in the
parts (+PARTS).

NEW PATTERN FUNCTIONALITY:

OLD:

* abjad.index_except()
* abjad.index_every()
* abjad.index_first()
* abjad.index_last()

NEW:

* abjad.index()

NEW PITCH FUNCTIONALITY:

* All Abjad pitch classes have been extensively refactored, cleaned up and
tested. Check the API entries for individual classes to see changes to
class initializers

* Added quartertone transposition.

* Closes 983

* Fixed 925:

OLD:

>>> abjad.NamedPitch(23.9)
NamedPitch("c''")  C5

FIXED:

>>> abjad.NamedPitch(23.9)
NamedPitch("c'''")  C6

* Closes 925

* Removed abjad.Registration; no longer supported

* Taught abjad.NamedPitch to initialize named quartertones:

>>> abjad.medPitch('Aqs6')

NEW RHYTHM-MAKER FUNCTIONALITY:

* The old rhythmmakertools package has been externalized and renamed
rmakers:

OLD: abjad.rhythmmakertools
NEW: abjad-ext-rmakers

* Visit https://github.com/Abjad/abjad-ext-rmakers to clone

* Added rmakers.SilenceMask.__invert__()
* Added rmakers.SustainMask.__invert__()

* Added rmakers.Talea.advance():

>>> talea = rmakers.Talea(
...     counts=[2, 1, 3, 2, 4, 1, 1],
...     denominator=16,
...     preamble=[1, 1, 1, 1],
...     )

>>> abjad.f(talea.advance(0))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1, 1, 1, 1],
)

>>> abjad.f(talea.advance(1))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1, 1, 1],
)

>>> abjad.f(talea.advance(2))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1, 1],
)

>>> abjad.f(talea.advance(3))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1],
)

>>> abjad.f(talea.advance(4))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
)

>>> abjad.f(talea.advance(5))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1, 1, 3, 2, 4, 1, 1],
)

>>> abjad.f(talea.advance(6))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[1, 3, 2, 4, 1, 1],
)

>>> abjad.f(talea.advance(7))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[3, 2, 4, 1, 1],
)

>>> abjad.f(talea.advance(8))
rmakers.Talea(
counts=[2, 1, 3, 2, 4, 1, 1],
denominator=16,
preamble=[2, 2, 4, 1, 1],
)

* Added abjad.Talea.period

* Added abjad.Talea.preamble; use for introductory counts prior to
repetition of talea counts

* Added TaleaRhythmMaker.__call__(..., state=None) keyword;
changed rhythm-maker rotation=None keyword to state=None dictionary;
pass {'rotation': value} to recoup previous interface

* Added abjad.Talea.__contains__()

* Taught TaleaRhythmMaker about previous weight consumed;
TaleaRhythmMaker publishes postcall state['talea_weight_consumed']

* Taught TaleaRhythmMaker to respect TupletSpecifier.diminution property:

Makes diminshed tuplets by default:

>>> rhythm_maker = rmakers.TaleaRhythmMaker(
...     extra_counts_per_division=[-1],
...     talea=rmakers.Talea(
...         counts=[1],
...         denominator=16,
...         ),
...     )

>>> divisions = [(1, 4), (1, 4)]
>>> selections = rhythm_maker(divisions)
>>> lilypond_file = abjad.LilyPondFile.rhythm(
...     selections,
...     divisions,
...     )

>>> abjad.f(lilypond_file[abjad.Staff])
\new RhythmicStaff
{
{   % measure
\times 2/3 {
c'8 [
c'8
c'8 ]
}
}   % measure
{   % measure
\times 2/3 {
c'8 [
c'8
c'8 ]
}
}   % measure
}

Makes augmented tuplets when ``diminution`` is set to false:

>>> rhythm_maker = rmakers.TaleaRhythmMaker(
...     extra_counts_per_division=[-1],
...     talea=rmakers.Talea(
...         counts=[1],
...         denominator=16,
...         ),
...     tuplet_specifier=rmakers.TupletSpecifier(
...         diminution=False,
...         ),
...     )

>>> divisions = [(1, 4), (1, 4)]
>>> selections = rhythm_maker(divisions)
>>> lilypond_file = abjad.LilyPondFile.rhythm(
...     selections,
...     divisions,
...     )

>>> abjad.f(lilypond_file[abjad.Staff])
\new RhythmicStaff
{
{   % measure
\tweak text tuplet-number::calc-fraction-text
\times 4/3 {
c'16 [
c'16
c'16 ]
}
}   % measure
{   % measure
\tweak text tuplet-number::calc-fraction-text
\times 4/3 {
c'16 [
c'16
c'16 ]
}
}   % measure
}

OLD:

* abjad.silence_except()
* abjad.silence_every()
* abjad.silence_first()
* abjad.silence_last()
* abjad.sustain_except()
* abjad.sustain_every()
* abjad.sustain_first()
* abjad.sustain_last()

NEW:

* rmakers.silence()
* rmakers.sustain()

OLD: rmakers.TupletSpecifier.flatten_trivial_tuplets
NEW: rmakers.TupletSpecifier.extract_trivial

OLD: rmakers.TupletSpecifier.preferred_denominator
NEW: rmakers.TupletSpecifier.denominator

OLD: rmakers.TupletSpecifier.rewrite_rest_filled_tuplets
NEW: rmakers.TupletSpecifier.rewrite_rest_filled

NEW SEGMENT-MAKER FUNCTIONALITY:

OLD: SegmentMaker.__call__()
NEW: SegmentMaker.run()

Motivation: makes SegmentMaker.__call__() available for command
accumulation inside definition.py files; command accumulation happens
many times; run() is called only once:

maker(
...
...
...
)

maker(
...
...
...
)

maker(
...
...
...
)

lilypond_file = maker.run()

NEW SELECTOR FUNCTIONALITY:

* Collapsed all of old selectiontools into single abjad.Selection class

* Collapsed all of old selectortools into same abjad.Selection class

* Integrated abjad.Expression into abjad.Selection

* Allows enchained, object-oriented partial evaluation of all methods.

* Defined concept of selection (and selection item) explicitly:

* SELECTION: an Abjad selection is an iterable of items such that each
item is an Abjad component or else another Abjad selection. The
definition allows for nested selections (but forbids the inclusion of
built-in tuples or lists as items in a selection)

* OLD: Selection.components

* NEW: Selection.items

* Replaced flatten=False keyword with Selection.flatten() method:

OLD: flatten keyword was required in most (but not all) methods to
return a flat selection of objects:

>>> abjad.select().by_leaf(flatten=True)  defaulted to flatten=False
>>> abjad.select().by_logical_tie(flatten=True)  def to flatten=True
>>> abjad.select().by_prototype(flatten=True)  def to flatten=False

NEW: all methods that say that they return a flat selection now do so:

>>> abjad.select().leaves()
>>> abjad.select().logical_ties()
>>> abjad.select().components()

* Replaced apply_to_each=False keyword with Selection.map() method:

OLD: apply_to_each keyword was required to get logical tie heads,
logical tie bodies, logical tie tails:

>>> abjad.select().by_logical_tie().get_item(
...     0,
...     apply_to_each=True,
...     )
>>> abjad.select().by_logical_tie().get_slice(
...     start=1,
...     apply_to_each=True,
...     )
>>> abjad.select().by_logical_tie().get_item(
...     -1,
...     apply_to_each=True,
...     )

NEW: Selector.map() is fully generalized; this cleans irregularities in
apply_to_each; composers can make arbitrarily mapped selections that
were not possible with apply_to_each:

>>> abjad.select().logical_ties().map(abjad.select()[0])
>>> abjad.select().logical_ties().map(abjad.select()[1:])
>>> abjad.select().logical_ties().map(abjad.select()[-1])

Note that object-oriented map() is now implemented as a bound method as
both abjad.Sequence.map() and abjad.Selection.map(). This allows for
functional composition of select expressions. Most common is a two-part
getter-map pattern; here are three examples:

Gets the leaves in each tuplet (as separate selections):

>>> getter = abjad.select().leaves()
>>> abjad.select().tuplets().map(getter)

Gets the first leaf in each tuplet:

>>> getter = abjad.select().leaf(0)
>>> abjad.select().tuplets().map(getter)

Gets the nonfirst leaves in each tuplet (as separate selections):

>>> getter = abjad.select().leaves()[1:]
>>> abjad.select().tuplets().map(getter)

All other combinations also work: mapping against contiguity, runs,
inequalites, etc.

* Generalized the select / filter interface:

NEW:

* abjad.select().filter()
* abjad.select().filter_duration()
* abjad.select().filter_length()
* abjad.select().filter_pitches()
* abjad.select().filter_preprolated()

The abjad.select().filter() method generalizes the filter interface;
composers may filter arbitrarily on other frozen select expressions or
on lambdas or arbitrary Python callables.

* Generalized the select / group-by interface:

NEW:

* abjad.select().group_by()
* abjad.select().group_by_contiguity()
* abjad.select().group_by_duration()
* abjad.select().group_by_length()
* abjad.select().group_by_measure()
* abjad.select().group_by_pitch()

The abjad.select().group_by() method generalizes the group-by
interface; composers may group arbitrarily on other frozen select
expressions or on lambdas or arbitrary Python callables.

* Added three new inequality factory functions for filter / group-by:

NEW:

* abjad.length()
* abjad.duration()
* abjad.pitches()

* Shortened by-phrase method names to direct object method names:

OLD:

* abjad.select().by_leaf()
* abjad.select().by_logical_tie()
* abjad.select().by_run()

NEW:

* abjad.select().leaves()
* abjad.select().logical_ties()
* abjad.select().runs()

* Paired singular / plural select methods:

NEW:

* abjad.select().leaves()
* abjad.select().leaf()

* abjad.select().logical_ties()
* abjad.select().logical_tie()

* abjad.select().runs()
* abjad.select().run()

* Generalized select __getitem__() to support Python get-item idioms
transparently:

OLD:

* abjad.select().first()
* abjad.select().last()
* abjad.select().most()
* abjad.select().middle()
* abjad.select().rest()

NEW:

* abjad.select()[0]
* abjad.select()[-1]
* abjad.select()[:-1]
* abjad.select()[1:-1]
* abjad.select()[1:]

* Extended abjad.Selection.__getitem__() to accept abjad.Pattern objects
for patterned item-getting

* Harmonized Iteration / Selection.
* Complete grace note iteration / selection integration
* Taught reverse iteration work with grace notes.
* Set iteration to include grace notes by default.
* Integrated grace_notes=False keyword into all select methods.

* Added Selector.template property.
* Added abjad.FormatSpecification.storage_format_forced_override property.
* Format templates are turned on in abjad.Selection; these allow for
maximally compact storage representations of frozen select expressions,
to arbitrary levels of functional composition through dot-chaining:

>>> abjad.f(abjad.select())
abjad.select()

>>> abjad.f(abjad.select().leaves())
abjad.select().leaves()

>>> abjad.f(abjhad.select().leaves()[1:-1])
abjad.select().leaves()[1:-1]

* Built out selector interface:

* abjad.select().chord()
* abjad.select().chords()
* abjad.select().components()
* abjad.select().contiguous()
* abjad.select().filter()
* abjad.select().filter_duration()
* abjad.select().filter_length()
* abjad.select().filter_pitches()
* abjad.select().flatten()
* abjad.select().group()
* abjad.select().group_duration()
* abjad.select().group_length()
* abjad.select().group_pitches()
* abjad.select().leaf()
* abjad.select().leaves()
* abjad.select().logical_measures()
* abjad.select().logical_ties()
* abjad.select().map()
* abjad.select().note()
* abjad.select().notes()
* abjad.select().partition_by_counts()
* abjad.select().partition_by_durations()
* abjad.select().partition_by_ratio()
* abjad.select().rest()
* abjad.select().rests()
* abjad.select().run()
* abjad.select().runs()
* abjad.select().top()
* abjad.select().tuplet()
* abjad.select().tuplets()

* Added abjad.Selector.color() for API illustration

* Added abjad.Selector.print() for API illustration

* Fixed longstanding abjad.iterate().by_logical_tie() bug

* Closes 850

* Closes 411

NEW SEQUENCE FUNCTIONALITY:

IMPORTANT SEARCH-AND-REPLACE FOR COMPOSERS PORTING TO ABJAD 3:

OLD:

>>> abjad.sequence().flatten()

NEW:

>>> abjad.sequence().flatten(depth=-1)

OLD: abjad.Sequence.flatten(depth=-1)
NEW: abjad.Sequence.flatten(depth=1) [changed negative index to positive]

* Added "enchained" partition:

sequence = abjad.sequence(range(16))
parts = sequence.partition_by_counts(
[5],
cyclic=True,
enchain=True,
overhang=True,
)
>>> for part in parts:
...     part
...
Sequence([0, 1, 2, 3, 4])
Sequence([4, 5, 6, 7, 8])
Sequence([8, 9, 10, 11, 12])
Sequence([12, 13, 14, 15])

* Added abjad.Selection.partition_by_counts(enchain=False) keyword

NEW SCHEME FUNCTIONALITY:

* Taught Scheme.format_scheme_value() more about pound-initiated strings:

staff = abjad.Staff("c'4 d' e' f'")
score = abjad.Score([staff])
abjad.tweak(staff[0].note_head).stencil = 'my-custom-stencil'
abjad.override(staff[1]).note_head.color = 'my-custom-stencil'
abjad.setting(score).mark_formatter = 'my-custom-mark-formatter'

abjad.f(score)
\new Score
\with
{
markFormatter = my-custom-mark-formatter
}
<<
\new Staff
{
\tweak stencil my-custom-stencil
c'4
\once \override NoteHead.color = my-custom-stencil
d'4
e'4
f'4
}
>>

* Closes 973

OLD: abjad.SchemeMoment.duration returned duration
NEW: abjad.SchemeMoment.duration returns nonreduced fraction

NEW SCORE PACKAGE FUNCTIONALITY

The new abjad.Path class provides an object-oriented interface to the
directory structure of Abjad score packages:

* Score package convenience properties:

* abjad.Path.builds
* abjad.Path.contents
* abjad.Path.distribution
* abjad.Path.etc
* abjad.Path.materials
* abjad.Path.segments
* abjad.Path.stylesheets
* abjad.Path.test
* abjad.Path.tools
* abjad.Path.wrapper
* abjad.Path._segments

* Score package path predicates:

* abjad.Path.is_build()
* abjad.Path.is_builds()
* abjad.Path.is_contents
* abjad.Path.is_distribution()
* abjad.Path.is_etc()
* abjad.Path.is_material()
* abjad.Path.is_materials()
* abjad.Path.is_segment()
* abjad.Path.is_segments()
* abjad.Path.is_stylesheets()
* abjad.Path.is_test()
* abjad.Path.is_tools()
* abjad.Path.is_wrapper()
* abjad.Path.is__segments()

* Score package path tree-handling methods:

* abjad.Path.get_next_package()
* abjad.Path.get_next_score()
* abjad.Path.get_previous_package()
* abjad.Path.get_previous_score()
* abjad.Path.list_paths()
* abjad.Path.segment_number_to_path()
* abjad.Path.with_name()
* abjad.Path.with_parent()
* abjad.Path.with_score()

* Score package name-handling:

* abjad.Path.coerce()
* abjad.Path.get_asset_type()
* abjad.Path.get_identifier()
* abjad.Path.get_name_predicate()
* abjad.Path.get_title()

* Metadata interface:

* abjad.Path.add_metadatum()
* abjad.Path.remove_metadatum()
* abjad.Path.update_order_dependent_segment_metadata()

* See the abjad.Path API entry for examples

NEW SPANNER FUNCTIONALITY:

OLD: abjad.Spanner.components
NEW: abjad.Spanner.leaves

NEW BEAM FUNCTIONALITY:

NEW:

* abjad.Beam.beam_lone_notes
* abjad.Beam.beam_rests
* abjad.Beam.durations
* abjad.Beam.span_beam_count
* abjad.Beam.stemlet_length

REMOVED:

* abjad.ComplexBeam
* abjad.DuratedComplexBeam
* abjad.MeasuredComplexBeam
* abjad.MultipartBeam

* Closes 319

NEW GLISSANDO PROPERITES:

Added abjad.Glissando.stems property:

>>> staff = abjad.Staff("c'8 d'8 e'8 f'8")
>>> glissando = abjad.Glissando(stems=True)
>>> abjad.attach(glissando, staff[:])

>>> abjad.f(staff)
\new Staff
{
c'8 \glissando
\hide NoteHead
\override NoteColumn.glissando-skip = t
\override NoteHead.no-ledgers = t
d'8 \glissando
e'8 \glissando
\revert NoteColumn.glissando-skip
\revert NoteHead.no-ledgers
\undo \hide NoteHead
f'8
}

Added abjad.Glissando.style property:

>>> staff = abjad.Staff("c'8 d'8 e'8 f'8")
>>> glissando = abjad.Glissando(style='trill')
>>> abjad.attach(glissando, staff[:])

>>> abjad.f(staff)
\new Staff
{
\override Glissando.style = 'trill
c'8 \glissando
d'8 \glissando
e'8 \glissando
\revert Glissando.style
f'8
}

OLD:

* Glissando.allow_repeat_pitches
* Glissando.parenthesize_repeated_pitches

NEW:

* Glissando.allow_repeats
* Glissando.parenthesize_repeats

NEW TEXT SPANNER FUNCTIONALITY: Simultaneous text spanners are now
available in a single voice:

>>> staff = abjad.Staff("c'4 d'4 e'4 fs'4")

>>> spanner_1 = abjad.TextSpanner(lilypond_id=1)
>>> abjad.attach(spanner_1, staff[:])
>>> spanner_1.attach(abjad.Markup('ord.').upright(), spanner_1[0])
>>> spanner_1.attach(abjad.ArrowLineSegment(), spanner_1[0])
>>> spanner_1.attach(abjad.Markup('pont.').upright(), spanner_1[-1])
>>> abjad.tweak(spanner_1).staff_padding = 2.5

>>> spanner = abjad.TextSpanner()
>>> abjad.attach(spanner, staff[:])
>>> spanner.attach(abjad.Markup('A').upright(), spanner[0])
>>> spanner.attach(abjad.ArrowLineSegment(), spanner[0])
>>> spanner.attach(abjad.Markup('B').upright(), spanner[-1])
>>> abjad.tweak(spanner).staff_padding = 5

>>> abjad.f(staff)
\new Staff
{
c'4
- \tweak staff-padding 2.5
- \tweak Y-extent f
- \tweak bound-details.left.text \markup {
\concat
{
\upright
ord.
\hspace
0.25
}
}
- \tweak arrow-width 0.25
- \tweak dash-fraction 1
- \tweak bound-details.left.stencil-align-dir-y center
- \tweak bound-details.right.arrow t
- \tweak bound-details.right-broken.padding 0
- \tweak bound-details.right-broken.text f
- \tweak bound-details.right.padding 0.5
- \tweak bound-details.right.stencil-align-dir-y center
- \tweak bound-details.right.text \markup {
\concat
{
\hspace
0.0
\upright
pont.
}
}
\startTextSpanOne
- \tweak staff-padding 5
- \tweak Y-extent f
- \tweak bound-details.left.text \markup {
\concat
{
\upright
A
\hspace
0.25
}
}
- \tweak arrow-width 0.25
- \tweak dash-fraction 1
- \tweak bound-details.left.stencil-align-dir-y center
- \tweak bound-details.right.arrow t
- \tweak bound-details.right-broken.padding 0
- \tweak bound-details.right-broken.text f
- \tweak bound-details.right.padding 0.5
- \tweak bound-details.right.stencil-align-dir-y center
- \tweak bound-details.right.text \markup {
\concat
{
\hspace
0.0
\upright
B
}
}
\startTextSpan
d'4
e'4
fs'4
\stopTextSpanOne
\stopTextSpan
}

Closes 932.

* Added text-spanner-id.ily from David Nalesnik to default.ly
stylesheet

NEW TIE FUNCTIONALITY:

* Taught abjad.Tie to allow enharmonic renotation:

>>> staff = abjad.Staff("c'4 bs c' dff'")
>>> abjad.attach(abjad.Tie(), staff[:])

>>> abjad.f(staff)
\new Staff
{
c'4 ~
bs4 ~
c'4 ~
dff'4
}

* Extended abjad.Tie.repeat property to allow repeat-tie threshold:

abjad.Tie objects may now format both conventional ties (~) and repeat-ties
(\repeatTie) on different leaves in the same tie.

Set the repeat-tie threshold as a duration inequality:

>>> tie = abjad.Tie(repeat=(1, 4))
>>> abjad.f(tie.repeat)
abjad.DurationInequality(
operator_string='>=',
duration=abjad.Duration(1, 4),
)

>>> staff = abjad.Staff("c'2 c'8 c'4.")
>>> abjad.attach(tie, staff[:])

>>> abjad.f(staff)
\new Staff
{
c'2
c'8
\repeatTie
~
c'4.
}

Durations that satisfy inequality can be said to "meet repeat-tie
threshold." Durations that do not meet repeat-tie threshold format
conventional tie on current note; durations that do meet repeat-tie
threshold format repeat-tie on following note.

LILYPOND FIX: abjad.Tie now corrects for down-positioned midstaff repeat ties.

abjad.Tie automatically tweaks repeat tie direction up when repeat tie connects
to long-duration note at staff position zero:

>>> tie = abjad.Tie(repeat=True)
>>> staff = abjad.Staff(r"b'4 b'4 b'2 b'1 b'\breve")
>>> abjad.attach(abjad.TimeSignature((8, 4)), staff[-1])
>>> abjad.attach(tie, staff[:])

>>> abjad.f(staff)
\new Staff
{
b'4
b'4
\repeatTie
b'2
\repeatTie
b'1
- \tweak direction up
\repeatTie
\time 8/4
b'\breve
- \tweak direction up
\repeatTie
}

* Removed abjad.ComplexTrillSpanner; use abjad.TrillSpanner instead

* Removed abjad.MetronomeMarkSpanner

* Removed abjad.Spanner.name property

* Unsubscribed all spanners from pieceiwse definition protocol

OLD: you could attach arbitrary information to spanners:

>>> beam = abjad.Beam()
>>> abjad.attach(99, beam)

NEW: you can no longer do this

OLD: abjad.HorizontalBracketSpanner
NEW: abjad.HorizontalBracket

OLD: abjad.Tie.repeat_ties
NEW: abjad.Tie.repeat

NEW STRICT FORMATTING / ATTACH-TAGGING FUNCTIONALITY:

Conventional (nonstrict) formatting positions some format contributions to
the right of leaves:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> abjad.attach(abjad.Articulation('^'), staff[0])
>>> abjad.attach(abjad.Markup('Allegro', direction=abjad.Up), staff[0])
>>> abjad.attach(abjad.StemTremolo(), staff[0])

>>> abjad.f(staff)
\new Staff {
c'4 :16 -\marcato ^ \markup { Allegro }
d'4
e'4
f'4
}

Strict formatting avoids right-slot formatting;
Strict formatting positions contributions strictly one-per-line;
Tags are introduced with %!;
IndicatorWrapper numbers tags (on a per-leaf basis) at attach-time;
Use to tag individual output lines for lexical postprocessing:

>>> staff = abjad.Staff("c'4 d' e' f'")
>>> abjad.attach(abjad.Articulation('^'), staff[0], tag='RED')
>>> markup = abjad.Markup('Allegro', direction=abjad.Up)
>>> abjad.attach(markup, staff[0], tag='GREEN')
>>> abjad.attach(abjad.StemTremolo(), staff[0], tag='BLUE')

>>> abjad.f(staff, strict=True)
\new Staff {
c'4
:16 %! BLUE:2
-\marcato %! RED:1
^ \markup { Allegro } %! GREEN:3
d'4
e'4
f'4
}

IMPORTANCE: attach tagging (which should always be combined with strict
formatting) enables to composes to tag the exact 'moment' at which any one
thing is attached to any other one thing. It doesn't matter what *type*
of thing is being attached to some other thing: it is rather an exact call
to abjad.attach() that is tagged when the tag keyword is set. This enables
generalized *(lexical) postprocessing* on the LilyPond files Abjad
produces: composer-authored Python (or other) scripts that selectively
activate and deactivate tagged lines down to any level of specificity.

Some examples:

* Courtesy metronome marks (at the start of a segment) can be tagged
for removal when segments are concatenated into a complete score

* Courtesy time signatures (at the start of a segment) can be tagged
the same way (for removal at segment concatenation time)

* Arbitrary amounts of (tagged) markup can be added to LilyPond files
now: markup showing spacing, clocktime duration, compositional
annotations and so on without limit can now be tag-attached to Abjad
leaves during composition and then removed by postprocessing scripts
at later stages of a composer's score-building toolchain

* Added abjad.f(..., strict=False) keyword.

* Added abjad.attach(..., tag=None) keyword.

* Added abjad.label(tag=None) keyword.

NEW STRING FUNCTIONALITY:

* Added abjad.String.is_shout_case()

* Added abjad.String.is_roman()

* Added abjad.String.sort_roman()

>>> strings = ['ViolinXI', 'ViolinX', 'ViolinIX']
>>> abjad.String.sort_roman(strings)
['ViolinIX', 'ViolinX', 'ViolinXI']

* Added abjad.String.strip_roman()

* Added abjad.String.to_indicator_stem()

* Added abjad.String.to_shout_case()

NEW TONAL ANALYSIS FUNCTIONALITY:

* Externalized abjad.tonalanalysistools to abjad-ext-tonality

* Visit https://github.com/Abjad/abjad-ext-tonality to clone

NEW TUPLET FUNCTIONALITY:

* All settable tuplet properties are now available at initialization:

OLD:

abjad.Tuplet.__init__(
multiplier=None,
components=Nonbe,
)

NEW:

abjad.Tuplet.__init__(
multiplier=None,
components=Nonbe,
denominator=None,
force_fraction=None,
hide=None,
)

* Added abjad.Tuplet.normalize_multiplier():

>>> tuplet = abjad.Tuplet((1, 3), "c'4 d' e'")
>>> abjad.f(tuplet)
\times 1/3 {
c'4
d'4
e'4
}

>>> tuplet.multiplier.normalized()
False

>>> tuplet.normalize_multiplier()
>>> abjad.f(tuplet)
\times 2/3 {
c'8
d'8
e'8
}

>>> tuplet.multiplier.normalized()
True

NEW: Added abjad.Tuplet.trivialize():

>>> tuplet = abjad.Tuplet((3, 4), "c'2")

>>> abjad.f(tuplet)
\tweak text tuplet-number::calc-fraction-text
\times 3/4 {
c'2
}

>>> tuplet.trivializable()
True

>>> tuplet.trivialize()

>>> abjad.f(tuplet)
{
c'4.
}

Changed LilyPond format of trivial (1:1) tuplets:

OLD:

>>> tuplet = abjad.Tuplet((1, 1), "c'8 d' e' ")
>>> abjad.f(tuplet)
{
c'8
d'8
e'8
}

NEW:

>>> tuplet = abjad.Tuplet((1, 1), "c'8 d' e'")
>>> abjad.f(tuplet)
\tweak text tuplet-number::calc-fraction-text
\times 1/1 {
c'8
d'8
e'8
}

This means that 1:1 tuplets now appear as explicit tuplets in output.

To recoup the old behavior, set abjad.Tuplet.hide to true:

>>> tuplet = abjad.Tuplet((1, 1), "c'8 d' e'", hide=True)
>>> abjad.f(tuplet)
\scaleDurations '(1 . 1) {
c'8
d'8
e'8
}

* Added abjad.Tuplet.rewrite_dots()
* Added abjad.Multiplier.from_dot_count()

Rewrites double dots as 7:4 prolation:

>>> tuplet = abjad.Tuplet(1, "c'8.. c'8..")
>>> abjad.f(tuplet)
\tweak text tuplet-number::calc-fraction-text
\times 1/1 {
c'8..
c'8..
}

>>> tuplet.rewrite_dots()
>>> abjad.f(tuplet)
\tweak text tuplet-number::calc-fraction-text
\times 7/4 {
c'8
c'8
}

Cleaned up tuplet constructors:

OLD:

Tuplet.from_duration_and_ratio(
duration,
ratio,
avoid_dots=True,
decrease_monotonic=True,
diminution=True,
)

NEW:

Tuplet.from_duration_and_ratio(
duration,
ratio,
decrease_monotonic=True,
)

OLD:

Tuplet.from_leaf_and_ratio(leaf, ratio, diminution=True)

NEW:

Tuplet.from_leaf_and_ratio(leaf, ratio)

OLD:

Tuplet.from_ratio_and_pair(
ratio,
fraction,
allow_trivial=False,
)

NEW:

Tuplet.from_ratio_and_pair(ratio, fraction)

OLD:

Tuplet.toggle_prolation() incorrectly changed trivial (1:1) prolation
to 1:2 prolation.

NEW:

Tuplet.toggle_prolation() leaves trivial (1:1) prolation unchanged.

* Closes 970

OLD: abjad.Tuplet.from_nonreduced_ratio_and_nonreduced_fraction()
NEW: abjad.Tuplet.from_ratio_and_pair()

OLD: abjad.Tuplet.invisible
NEW: abjad.Tuplet.hide

OLD: abjad.Tuplet.is_augmentation (property)
NEW: abjad.Tuplet.augmentation() (method)

OLD: abjad.Tuplet.is_diminution (property)
NEW: abjad.Tuplet.diminution() (method)

OLD: abjad.Tuplet.is_redundant (property)
NEW: abjad.Tuplet.trivializable() (method)

OLD: abjad.Tuplet.is_trivial (property)
NEW: abjad.Tuplet.trivial() (method

OLD: abjad.Tuplet.preferred_denominator
NEW: abjad.Tuplet.denominator

NEW TWEAK FUNCTIONALITY:

* Added tuplet tweaks:

>>> tuplet_1 = abjad.Tuplet((2, 3), "c'4 ( d'4 e'4 )")
>>> abjad.tweak(tuplet_1).color = 'red'
>>> abjad.tweak(tuplet_1).staff_padding = 2

>>> tuplet_2 = abjad.Tuplet((2, 3), "c'4 ( d'4 e'4 )")
>>> abjad.tweak(tuplet_2).color = 'green'
>>> abjad.tweak(tuplet_2).staff_padding = 2

>>> tuplet_3 = abjad.Tuplet((5, 4), [tuplet_1, tuplet_2])
>>> abjad.tweak(tuplet_3).color = 'blue'
>>> abjad.tweak(tuplet_3).staff_padding = 4

>>> staff = abjad.Staff([tuplet_3])
>>> leaves = abjad.select(staff).leaves()
>>> abjad.attach(abjad.TimeSignature((5, 4)), leaves[0])
>>> literal = abjad.LilyPondLiteral(r'\set tupletFullLength = t')
>>> abjad.attach(literal, staff)

>>> abjad.f(staff)
\new Staff
{
\set tupletFullLength = t
\tweak text tuplet-number::calc-fraction-text
\tweak color blue
\tweak staff-padding 4
\times 5/4 {
\tweak color red
\tweak staff-padding 2
\times 2/3 {
\time 5/4
c'4
(
d'4
e'4
)
}
\tweak color green
\tweak staff-padding 2
\times 2/3 {
c'4
(
d'4
e'4
)
}
}
}

* Added indicator tweaks:

* Arpeggio
* Articulation
* BendAfter
* ColorFingering
* Dynamic
* Fermata
* LaissezVibrer
* LilyPondLiteral
* Staccatissimo
* Staccato
* BreathMark
* KeySignature
* RehearsalMark

ARPEGGIO TWEAKS:

>>> chord = abjad.Chord("<c' e' g' c''>4")
>>> arpeggio = abjad.Arpeggio()
>>> abjad.tweak(arpeggio).color = 'blue'
>>> abjad.attach(arpeggio, chord)

>>> abjad.f(chord)
<c' e' g' c''>4
- \tweak color blue
\arpeggio

ARTICULATION TWEAKS:

>>> note = Note("c'4")
>>> articulation = abjad.Articulation('marcato')
>>> abjad.tweak(articulation).color = 'blue'
>>> abjad.attach(articulation, note)

>>> abjad.f(note)
c'4
- \tweak color blue
-\marcato

DYNAMIC TWEAKS:

>>> note = Note("c'4")
>>> dynamic = abjad.Dynamic('f')
>>> abjad.tweak(dynamic).color = 'blue'
>>> abjad.attach(dynamic, note)

>>> abjad.f(note)
c'4
- \tweak color blue
\f

* Closes 957

* Added support for tweak expressions; use the abjad.tweak() factory
function:

>>> abjad.tweak('red').color
LilyPondTweakManager(('color', 'red'))

>>> abjad.tweak(6).Y_offset
LilyPondTweakManager(('Y_offset', 6))

>>> abjad.tweak(False).bound_details__left_broken__text
LilyPondTweakManager(('bound_details__left_broken__text', False))

GLOBAL NAME CHANGES:

OLD: decrease_durations_monotonically
NEW: decrease_monotonic

OLD: forbidden_written_duration
NEW: forbidden_duration

OLD: is_once
NEW: once

OLD: use_messiaen_style_ties
NEW: repeat_ties

CHANGES FOR DEVELOPERS:

* Integrated pathlib

OLD:

* abjad.agenttools.InspectionAgent
* abjad.agenttools.IterationAgent
* abjad.agenttools.LabelAgent
* abjad.agenttools.MutatinAgent
* abjad.agenttools.PersistenceAgent

NEW:

* abjad.Inspection
* abjad.Iteration
* abjad.Label
* abjad.Mutation
* abjad.PersistenceManager

OLD: abjad.Component._indicator_wrappers
NEW: abjad.Component._wrappers

OLD: abjad.StorageFormatAgent
NEW: abjad.StorageFormatManager

OLD: abjad.IndicatorWrapper
NEW: abjad.Wrapper

OLD: abjad.Wrapper.is_annotation
NEW: abjad.Wrapper.annotation

3.0.0rc1


      

2.21

- `Pitch.named_pitch`
- `Pitch.named_pitch_class`
- `Pitch.numbered_pitch`
- `Pitch.numbered_pitch_class`
- `Pitch.pitch_class_name`
- `Pitch.pitch_class_number`
- `Pitch.pitch_class_octave_label`
- `Pitch.pitch_name`
- `Pitch.pitch_number`
- `Retrogression.period`
- `Rotation.period`

Bugfixes

- Fixed definition of `pitchtools.NumberedPitch.multiply()`.
- Fixed edge-cases in `abjad.new(expression)`.
- Taught `rhythmmakertools.BeamSpecifier` to respect `beam_rests` setting.

2.20

Improvements (global)

- Greatly expanded Abjad global namespace:

['Accelerando', 'Annotation', 'Arpeggio', 'Arrow', 'Articulation',
'BarLine', 'Beam', 'BendAfter', 'BowContactPoint', 'BowContactSpanner',
'BowMotionTechnique', 'BowPressure', 'BreathMark', 'Chord', 'Clef',
'ClefSpanner', 'Cluster', 'ColorFingering', 'ComplexBeam',
'ComplexTrillSpanner', 'Component', 'Container', 'Context', 'Crescendo',
'CyclicTuple', 'Decrescendo', 'DuratedComplexBeam', 'Duration', 'Dynamic',
'Expression', 'Fermata', 'FixedDurationTuplet', 'Fraction',
'GeneralizedBeam', 'Glissando', 'GraceContainer', 'Hairpin',
'HiddenStaffSpanner', 'HorizontalBracketSpanner', 'Infinity', 'Inversion',
'KeyCluster', 'KeySignature', 'LaissezVibrer', 'Leaf', 'LilyPondCommand',
'LilyPondComment', 'LilyPondFile', 'LilyPondLiteral', 'LineSegment',
'Markup', 'MarkupList', 'Measure', 'MeasuredComplexBeam', 'Meter',
'MetricModulation', 'MultimeasureRest', 'MultipartBeam', 'Multiplication',
'Multiplier', 'NamedInterval', 'NamedIntervalClass', 'NamedPitch',
'NamedPitchClass', 'NonreducedFraction', 'NonreducedRatio', 'Note',
'NumberedInterval', 'NumberedIntervalClass', 'NumberedPitch',
'NumberedPitchClass', 'OctavationSpanner', 'Offset', 'PageBreak',
'Pattern', 'PhrasingSlur', 'PianoPedalSpanner', 'PitchClassSegment',
'PitchClassSet', 'PitchRange', 'PitchSegment', 'PitchSet', 'Ratio',
'Registration', 'RehearsalMark', 'Repeat', 'Rest', 'Retrograde',
'Ritardando', 'Rotation', 'Scheme', 'SchemeMoment', 'SchemePair',
'SchemeSymbol', 'SchemeVector', 'Score', 'Selection', 'Selector',
'Sequence', 'SetClass', 'Skip', 'Slur', 'Spanner', 'Staff', 'StaffChange',
'StaffGroup', 'StaffLinesSpanner', 'StemTremolo', 'StemTremoloSpanner',
'StringContactPoint', 'StringNumber', 'SystemBreak', 'Tempo',
'TempoSpanner', 'TextSpanner', 'Tie', 'TimeSignature', 'Timespan',
'Transposition', 'Tremolo', 'TrillSpanner', 'Tuning', 'Tuplet',
'TwelveToneRow', 'TypedOrderedDict', 'Voice', 'abctools',
'abjad_configuration', 'agenttools', 'attach', 'commandlinetools',
'datastructuretools', 'demos', 'detach', 'documentationtools',
'durationtools', 'exceptiontools', 'expressiontools', 'ext', 'f', 'graph',
'indicatortools', 'inspect_', 'instrumenttools', 'ipythontools', 'iterate',
'label', 'lilypondfiletools', 'lilypondnametools', 'lilypondparsertools',
'ly', 'markuptools', 'mathtools', 'metertools', 'mutate', 'new',
'override', 'parse', 'patterntools', 'persist', 'pitchtools', 'play',
'print_function', 'quantizationtools', 'rhythmmakertools',
'rhythmtreetools', 'schemetools', 'scoretools', 'select', 'select_all',
'select_every', 'select_first', 'select_last', 'selectiontools',
'selectortools', 'sequence', 'sequencetools', 'set_', 'show', 'silence',
'silence_all', 'silence_every', 'silence_except', 'silence_first',
'silence_last', 'spannertools', 'stringtools', 'sustain', 'sustain_all',
'sustain_every', 'sustain_first', 'sustain_last', 'systemtools',
'templatetools', 'timespantools', 'tonalanalysistools', 'topleveltools',
'tweak']

- Integrated `quicktions` library for optional C-accelerated calculations with
rational numbers. Install via `pip install abjad[accelerated]`.
- Taught `systemtools.ImportManager` about Cython `.pyx` files.
- Extended scripts:
- Added `ajv doctest` `--abjad-only` option to suppress implicit `from
abjad import *` imports at the beginning of each doctest.
- Taught `ajk script` to ignore EPS files and private `_doc` directories.
- Taught `ajk script` to ignore private `_doc` directories.

Improvements (by package):

- **`expressiontools`**:
- Collapsed `expressiontools.Callback`, `expressiontools.LabelExpression`,
and `expressiontools.SequenceExpression` into
`expressiontools.Expression`.
- Added `expressiontools.Expression.__eq__()`.
- Added `expressiontools.Signature` decorator.

- **`indicatortools`**:
- Added `indicatortools.LilyPondLiteral`.
- Added `sfffz` to known dynamic names.

- **`lilypondfiletools`**:
- Added `lilypondfiletools.LilyPondFile` anonymous context retrieval.
- Added `lilypondfiletools.LilyPondFile` custom context retrieval.

- **`markuptools`**:
- Added `markuptools.MarkupInventory.center_column()`.
- Added `markuptools.MarkupInventory.combine()`.
- Added `markuptools.MarkupInventory.concat()`.
- Added `markuptools.MarkupInventory.__illustrate__()`.
- Added `markuptools.MarkupInventory.__lt__()`.

- **`mathtools`**:
- Added `Infinity.__float__()`.

- **`patterntools`**:
- Added `patterntools.CompoundPattern.get_matching_items()`.
- Added `patterntools.Pattern.get_matching_items()`.

- **`pitchtools`**:
- Added `NumberedPitch.from_pitch_class_octave()`.
- Added `Pitch.pitch_class`.
- Added `Pitch.name`.
- Added `Pitch.number`.
- Added `PitchClassSet.__illustrate__()`.
- Added `PitchClassSegment.permute(row=None)`.
- Added `PitchClassSegment.to_pitch_classes()`.
- Added `PitchClassSegment.to_pitches()`.
- Added `PitchSegment.to_pitch_classes()`.
- Added `PitchSegment.to_pitches()`.
- Added `PitchSet.__illustrate__()`.
- Added `SetClass`.

- **`scoretools`**:
- Added `scoretools.make_leaves(skips_instead_of_rests=False)` keyword.
- Taught `scoretools.Container` to initialize mixed selection / component
input.

- **`selectortools`**:
- Added `Selector.group_by_pitch()`.
- Added `Selector.by_leaf(head=None)` keyword.
- Added `Selector.by_leaf(pitched=True)` keyword.
- Added `Selector.by_leaf(prototype=None)` keyword.
- Added `Selector.by_leaf(tail=None)` keyword.
- Added `Selector.by_leaf(trim=None)` keyword.
- Added `Selector.select()`.

- **`sequencetools`**:
- Added `sequencetools.Enumeration` class:
- `Enumeration.yield_combinations()`
- `Enumeration.yield_outer_product()`
- `Enumeration.yield_pairs()`
- `Enumeration.yield_partitions()`
- `Enumeration.yield_permutations()`
- `Enumeration.yield_subsequences()`
- Added `Sequence.reverse(recurse=False)` keyword.
- Added `Sequence.sort()` method.

- **`systemtools`**:
- Taught `systemtools.StorageFormatAgent` about keyword-only parameters.

- **`timespantools`**:
- Added `timespantools.Timespan.__illustrate__(scale=None)` keyword.
- Added `timespantools.TimespanInventory.__invert__()`.

- **`topleveltools`**:
- Added `topleveltools.tweak()` function.

Changes (global)

- Changed `_lilypond_format property` to `_get_lilypond_format()` method.
- Changed `args` to `arguments` in most places in the codebase.
- Changed `expr` to `argument` in most places in the codebase.
- Changed `kwargs` to `keywords` in most places in the codebase.
- Changed `note head` to `note-head` globally.
- Taught `LilyPondOutputProxy` to omit empty layout and paper blocks.

Changes (by package)

- **`datastructuretools`**:
- Removed `datastructuretools.Matrix`.
- Removed `datastructuretools.CyclicMatrix`.
- Replaced `TypedTuple.__getslice__()` with `__getitem__()`.
- Rewired `CyclicTuple` to remove multiple inheritance.

- **`documentationtools`**:
- All `Graphviz*` classes have been moved into a new `graphtools`
subpackage.

- **`durationtools`**:
- Removed `durationtools.Division`.

- **`indicatortools`**:
- Changed `ClefInventory` to `ClefList`.
- Moved `ClefInventory` from `indicatortools` to `instrumenttools`.
- Removed `IsAtSoundingPitch`.
- Removed `IsUnpitched`.
- Removed `SpacingIndication`.

- **`lilypondfiletools`**:
- Removed `lilypondfiletools.make_basic_lilypond_file()`.
Use `lilypondfiletools.LilyPondFile.new()` instead.

- **`markuptools`**:
- Removed `Markup.make_*()` methods.
- Changed `Markup.super_()` to `Markup.super()`.
- Changed `Markup.line()` to static method.
- Changed `MarkupInventory` to `MarkupList`.
- Changed the following methods to accept markup list:
- `Markup.center_column(markup_list)`
- `Markup.column(markup_list)`
- `Markup.concat(markup_list)`
- `Markup.left_column(markup_list)`
- `Markup.line(markup_list)`
- `Markup.overlay(markup_list)`
- `Markup.right_column(markup_list)`
- Changed `Markup` interpreter representation
- OLD: `Markup(contents=('Allegro assai',))`
- NEW: `Markup(contents=['Allegro assai'])`
- Changed Markup.combine() signature:
- OLD: `Markup.combine(markup_1, markup_2)`
- NEW: `Markup.combine([markup_1, markup_2])`
- Reimplemented all `markuptools` pytests as doctests.

- **`pitchtools`**:
- Removed `PitchArray*` classes.
- Changed `Rotation.transpose` default from `True` to `None`.
- Changed `has_duplicates` property to method:
- `IntervalClassSegment.has_duplicates()`
- `IntervalSegment.has_duplicates()`
- `PitchClassSegment.has_duplicates()`
- `PitchSegment.has_duplicates()`
- `Segment.has_duplicates()`
- `TwelveToneRow.has_duplicates()`
- Renamed `Octave.octave_number` to `Octave.number`.
- Renamed `Octave.octave_tick_string` to `Octave.tick_string`.
- Renamed `Pitch.octave_number` to `Pitch.octave.number`.
- Renamed `.index` to `.n`:
- `Multiplication.index` to `Multiplication.n`
- `Rotation.index` to `Rotation.n`
- `Transposition.index` to `Transposition.n`
- Renamed `PitchOperation` to `CompoundOperator`.
- Renamed `Retrogression` to `Retrograde`.
- Named Stravinsky-style rotation explicitly:
- `PitchClassSegment.rotate(n=-1, transpose=True)` to
`PitchClassSegment.rotate(n=-1, stravinsky=True)`
- `TwelveToneRow.rotate(n=-1, transpose=True)` to
`TwelveToneRow.rotate(n=-1, stravinsky=True)`

- **`selectortools`**:
- Renamed `select_all_but_first_logical_tie_in_pitched_runs()` to
`select_nonfirst_logical_tie_in_pitched_runs()`.
- Renamed `select_all_but_last_logical_tie_in_pitched_runs()` to
`select_nonlast_logical_tie_in_pitched_runs()`.

- **`sequencetools`**:

- Changed all `sequencetools` functions to `Sequence` methods.
- Renamed `Sequence.rotate(index=0)` to `Sequence.rotate(n=0)`.
- Renamed `sequencetools.join_subsequences()` to `Sequence.join()`.
- Renamed `sequencetools.permute_sequence()` to `Sequence.permute()`.
- Renamed `sequencetools.split_sequence()` to `Sequence.split()`.
- Renamed `sequencetools.flatten_sequence()` to `Sequence.flatten()`.
- Renamed `sequencetools.reverse_sequence()` to `Sequence.reverse()`.
- Renamed `sequencetools.rotate_sequence()` to `Sequence.rotate()`.
- Renamed `sequencetools.repeat_sequence()` to `Sequence.repeat()`.
- Renamed `sequencetools.repeat_sequence()` to `Sequence.repeat()`.
- Renamed `sequencetools.partition_sequence_by_counts()` to
`Sequence.partition_by_counts()`.
- Renamed `sequencetools.zip_sequences()` to `Sequence.zip()`.
- Renamed `sequencetools.truncate_sequence()` to `Sequence.truncate()`.
- Renamed `sequence.partition_sequence_by_value_of_elements()` to
`Sequence.group_by()`.
- Renamed `sequencetools.partition_sequence_by_ratio_of_lengths()` to
`Sequence.partition_by_ratio_of_lengths()`.
- Renamed `sequencetools.partition_sequence_by_ratio_of_weights()` to
`Sequence.partition_by_ratio_of_weights()`.
- Renamed `sequencetools.partition_sequence_by_weights()` to
`Sequence.partition_by_weights()`.
- Renamed `sequencetools.iterate_sequence_nwise()` to
`Sequence.nwise()`.
- Renamed `sequencetools.sum_consecutive_elements_by_sign()` to
`Sequence.sum_by_sign()`.
- Renamed `sequencetools.repeat_sequence_to_length()` to
`Sequence.repeat_to_length()`.
- Renamed `sequencetools.repeat_sequence_to_weight()` to
`Sequence.repeat_to_weight()`.
- Renamed `sequencetools.remove_repeated_elements()` to
`Sequence.remove_repeats()`.
- Renamed `sequencetools.remove_elements()` to `Sequence.remove()`.
- Renamed `sequencetools.replace_elements()` to `Sequence.replace()`.
- Renamed `sequencetools.retain_elements()` to `Sequence.retain()`.
- Renamed `sequencetools.Enumeration.yield_pairs()` to
`Enumeration.yield_outer_product()`.
- Renamed `Enumeration.yield_unordered_pairs()` to
`Enumeration.yield_pairs()`.
- Removed the following sequencetools functions (because composer-specific
or too complex to belong in the public API):
- `sequencetools.join_subsequences_by_sign_of_elements()`
- `sequencetools.increase_elements()`
- `sequencetools.interlace_sequences()`
- `sequencetools.iterate_sequence_boustrophedon()`
- `sequencetools.negate_elements()`
- `sequencetools.overwrite_elements()`
- `sequencetools.partition_sequence_by_sign_of_elements()`
- `sequencetools.remove_subsequence_of_weight_at_index()`
- `sequencetools.repeat_elements()`
- `sequencetools.splice_between_elements()`

- **`spannertools`**:

- Renamed `Glissando.allow_repeated_pitches` to `Glissando.allow_repeat_pitches`.

- **`systemtools`**:

- Removed `Memoize` decorator.
- Changed `_storage_format_specification` property to
`_get_storage_format_specification()` method.
- Changed `IOManager.profile_expr()` to `IOManager.profile()`.

Deprecated

2.19

Improvements
- [740] Proofread ‘For beginners’ section of docs. Thanks delucis!
- [741] LilyPond's log is displayed on LilyPond compilation error.
- [749] Added `LilyPondCommand` to Abjad's global namespace.
- [752] Taught `detach()` about different types of grace container.
- [753] Taught `Dynamics` about _sforzando_ dynamic names.
- [744] Refactoring and cleanup in `schemetools`. Thanks ajyoon!
- [745] Reimplemented Abjad's repr, storage format and object templating system.
- [755, 760, 761, 763] Cleaned-up class member ordering via a new command-line script. Thanks ajyoon!
- [766] Abjad's `ajv` looks for `.ajv` config files in $HOME, the current directory and any parent directory thereof. A [doctest] section can specify imports to be run at the beginning of each doctest in `ajv doctest` via an `imports` key.

Changes
- [745] `AbjadObject`'s `_storage_format_specification` and `_repr_specification` properties are deprecated in favor of a new unified `_get_format_specification()` method. The old properties will no longer be supported in the next Abjad release: 2.20.

Bugfixes
- [762] Improved interaction between chords and ties. Thanks quesebifurcan!
- [764] `IOManager.open_file()` respects Abjad's config when opening MIDI files. Thanks quesebifurcan!
- [767] Fixed edge-cases in Graphviz attribute-handling and append/extend behavior.
- [774] NamedPitch.invert() guards against errors from multiply-augmented or diminished intervals.

2.18

Improvements
- [715] Grace notes are now aware of their parentage above and beyond their enclosing `GraceContainer`.
- [703] Added a `synthetic_offset` keyword to `attach()` and `IndicatorExpression`.
- This allows composers to attach effective indicators which start _before_ the score starts, allowing composers to model how various indicators hold over from a previous section.
- For example: `attach(Dynamic('f'), voice, synthetic_offset=-1)`
- [640] Many optimizations:
- Optimized hot code paths in Duration and Offset instantiation.
- Optimized timespan comparison.
- Optimized parser instantiation.
- [626, 726] `Dynamic` now recognizes `sffp`, `sffz` and `niente` as valid dynamic names.
- [726] `Hairpin` is now niente-aware.
- [628] Implemented a collection of command-line tools for working with score packages.
- `ajv score`, `ajv material`, `ajv segment`, `ajv target`.
- These tools are provisional pending more extensive use.
- [719] Implemented `TupletSpellingSpecifier.preferred_denominator`.
- This mirrors `Tuplet.preferred_denominator`.
- [666] Abjad now formats LilyPond properties using the newer dot-chained syntax, not the old `'...` syntax.
- As always, Abjad targets the development version of LilyPond, currently the 2.19 series.
- [720] Non-reduced fractions can be attached to leaves just like `Multiplier` objects.
- [724] Equipped `Sequence.partition_by_counts()` with a `reverse` keyword.
- [725] Equipped `Markup` with `.sub()` and `.super()` markup command methods.

Changes
- [709] Spanners now attach **only to leaves**, **never to containers**.
- This represents a significant simplification in Abjad's score model.
- This change should generally be transparent to users, as `attach()` does the work of iterating leaves in its component expression.
- [709] Many spanners now attach only to 2 or more leaves at once, including `Slur` and `Tie`.
- [709] Removed `Container.select_leaves()`.
- To select a container's leaves, use `list(iterate(container).by_leaf())`.
- To select the _first leaf_ of a container, use `next(iterate(container).by_leaf())`.
- To select the _last leaf_ of a container, use `next(iterate(container).by_leaf(reverse=True))`.
- [628, 644, 643, 702] Tool-chain clean-up and refactoring
- `developerscripttools` was renamed `commandlinetools`.
- `DeveloperScript` was renamed `CommandlineScript`.
- Unused and obsolete scripts have been removed.
- [717] Out-ported various classes to https://github.com/trevorbaca/baca:
- `Cursor`, `PitchClassTree`, `PayloadTree`, `CyclicPayloadTree`.
- [714] `Selector.by_leaves()` is now `Selector.by_leaf()`. This harmonizes with `IterationAgent.by_leaf()`.
- [721] Added a new LilyPondCommand.prefix property.
- This makes it easy to position arbitrary strings relative to components.

Bugfixes
- [655] `Tuplet._simplify_redundant_tuplet()` takes logical ties into account.
- [680] Abjad's docs post-process SVG images under Python 2.7 properly.
- [736] Registration now takes quarter tones into account.

2.17

Improvements:
- Abjad's documentation has seen extensive work, including:
- a new theme based off of https://github.com/snide/sphinx_rtd_theme
- crisp SVG Graphviz output for all class lineage graphs
- expandable image thumbnails
- a score gallery page showing notation examples from many scores created with Abjad
- revised installation instructions
- virtually all code examples in the docs are now interpreted via the `abjad-book` Sphinx extension, guaranteeing correctness
- many enhancements to Abjad's Sphinx extension
- A new `PackageGitCommitToken` class for embedding Python package version information in LilyPond files.
- A new `IterationAgent.by_timeline_and_logical_tie()` method.
- A provisional collection of new classes in `lilypondnametools` for object-modeling various LilyPond entities: `LilyPondGrob`, `LilyPondGrobInterface`, `LilyPondContext` and `LilyPondEngraver`.
- Abjad now supports PyPy.

Changes:
- Abjad's dependencies have been separated into `standard`, `development` and `ipython`. See our installation docs for details.
- Abjad's IPython extension now uses `timidity` instead of `ffmpeg` and `fluidsynth` to embed audio output from calls to `play()`. OSX users can install `timidity` via HomeBrew.
- `GraphvizEdge.__call__()` has been removed in favor of explicit `.attach(node_one, node_two)` and `.detach()` methods.
- `sievetools` functionality has been merged into the classes housed in `patterntools`.
- All `labeltools` functionality has been migrated into the `agentools.LabelAgent` class.
- `LilyPondFile` properties such as `paper_size`, `includes`, etc. are now immutable. Set them during instantiation. See the API example for details.
- `TimeSignature` can now only be instantiated from a pair, such as `(3, 4)`: `TimeSignature((3, 4))`.

Bugfixes:
- `GraphvizGraph` instances can now be copied with edges intact.
- `Markup` now properly quotes strings containing `` symbols.

2.16


      

2.15


      

2.14